Tutorial de PERL en castellano : Regularizando la situación |
Pero ya puestos... Preguntas frecuentemente preguntadas Bibliografía. Recursos Internet Presentando lo impresentable |
Una forma todavía más directa de trabajar con estos ficheros de texto que tienen una estructura regular (que son la mayoría), es usar expresiones regulares. Una expresión regular es una forma de expresar gramaticalmente la estructura de cualquier cadena alfanumérica. Por ejemplo, una cadena compuesta por una letra inicial, con una letra o un número a continuación se podría expresar de la forma siguiente
donde |
expresa una alternativa y *
indica que puede aparecer 0 o más veces. Pues bien, estas expresiones
regulares se utilizan enormemente en PERL (de hecho ya hemos usado una,
aunque compuesta por un solo carácter, en la línea 4 del programa
anterior), y cada vez que hemos usado split
. Las
expresiones regulares se usan para hacer comparaciones, es decir,
hallar si un texto sigue o contiene una determinada expresión regular,
y también para sustituir una subcadena que cumpla una expresión
regular por otra cadena.
La expresión regular más simple es la propia cadena con
la que se quiere comparar; es decir, una cadena coincidirá consigo
misma. Las expresiones regulares en PERL siempre van
encerradas entre //
, o bien m{}
(las llaves
pueden ser sustituidas por cualquier otro elemento que no esté
incluido en la expresión regular),
y si no se indica nada, comparará la expresión regular con
la variable por defecto, $_
, es decir, que
/pepe/
será cierto si $_
contiene íntegra la cadena pepe
. El
programilla
$_ = "pepeillo"; print "si" if /pepe/;
imprimirá
si
Las expresiones regulares usan símbolos convencionales para referirse
a grupos de caracteres y otros para repetición o señales
de puntuación. En algunos casos, si el símbolo significa
algo dentro de la expresión regular (o en PERL), se precede por
\
(escape). Por ejemplo, . significa "cualquier
carácter" dentro de una expresión regular; luego para referirnos
al punto como tal, usaremos \.
. También se usa \/,\?
y \*
, por ejemplo. Otras expresiones
más complicadas incluirían repeticiones de símbolos;
por ejemplo, \w+
casaría con cualquier
palabra (un carácter alfanumérico repetido uno o más
veces).
|
Para agrupar símbolos se usa el paréntesis,
que además sirve para indicarle a PERL que con
lo que coincida con la expresión en su
interior se va a hacer algo. Para empezar, se asigna a la variable $&
;
pero además, podemos asignar a otra variable el resultado de la
comparación; en general, una comparación con una expresión
regular que incluya paréntesis devuelve una lista con todo lo que
coincida con el contenido de los paréntesis
$zipi = "Pepeillo Gonzalez MacKenzie 8000";
@parejas = ($zipi =~ /(\D+) (\d+)/);
(el
nombre de la variable y el símbolo =~
se pueden suprimir si la comparación se hace sobre la variable por
defecto $_
); @parejas
contendrá ("Pepeillo Gonzalez MacKenzie",8000),
ya que la primera expresión regular indica
"uno o más caracteres no numéricos", mientras que la segunda
representa "uno o más caracteres numéricos".
Por ejemplo, el fichero de paganinis usado en ejemplos anteriores anterior
tiene la estructura siguiente: una o más palabras, separadas por
un espacio, una cantidad (números y puntos), una hora (números
y dos puntos) y una fecha (números y /). Esto se dice en PERL mediante
la expresión siguiente(\D+) (\d+\.?\d+) (\S+)
(\d+)\/(\d+)\/(\d+)
que puede parecer un poco críptica (y
en realidad lo es), pero
cuyo significado se puede resolver mirando la tabla
4. Con esta modificación, el bucle central del programa totales.pl se queda reducido a
while(<>) {
($paganini, $pasta, $hora, $dia, $mes) = /(\D+) (\d+\.?\d+) (\S+) (\d+)\/(\d+)\/(\d+)/;
$totalDia{"$mes-$dia"}+=$pasta;
}
Más compacidad no se puede pedir. Además, ya de camino, nos vamos ahorrando algunas variables. En la primera línea del interior del bucle se asigna la parte de la cadena que coincide con lo descrito en el interior de los paréntesis a cada una de las variables, es decir, divide la línea en cinco campos, cada uno de los cuales tiene un tipo diferente. Hay cinco pares de paréntesis, para cinco variables. Veamos cada expresión regular por partes.
|
La primera expresión regular es \D+
.
Como se ve en la tabla 4, \D
coincide con todo lo que no sea numérico, y en particular letras
y espacios. Este campo es problemático, porque puede incluir una
o varias palabras; sin embargo, sabemos que el siguiente campo comienza
por un número; por tanto, incluiremos en este campo todo lo que
haya hasta que encontremos un número. Se puede insertar el código
siguiente print $paganini;
en el bucle para ver qué
se incluye dentro de ese campo.
La siguiente expresión regular, (\d+\.?\d+)
,
no describe otra cosa que un número real en notación de coma
flotante, es decir, una o más cifras (\d+)
,
seguidas o no por un punto (\.?
, no olvidar
que el punto tiene significado especial en las expresiones regulares),
que a su vez debe de estar seguido por una o más cifras (\d+)
.
Esta expresión regular coincide además con aquellos números
sin punto enmedio.
Y la siguiente \S+
coincide con la hora,
aunque quizás podría coincidir con cualquier cosa, simplemente
coge todo lo que haya entre los dos espacios. En realidad, si pusiéramos
toda la expresión regular anterior como(\S+) (\S+) (\S+)
(\S+)
funcionaría perfectamente.
Y para terminar,(\d+)\/(\d+)\/(\d+)
coincide con
el día, el mes y el año, que están separados por una barra diagonal.
Con estas expresiones regulares se pueden construir programas superpotentes, que convierten casi cualquier texto en casi cualquier otro. Incluso, si uno se atreve (nuestro político corrupto no creemos que se atreva) un analizador sintáctico de un lenguaje de alto nivel, aunque sea simplificado. Otra utilidad es hacer filtros de correo electrónico, o de noticias de USENET (de hecho, el killfile o fichero que incluye todas las expresiones regulares de mensajes que deben de ser borrados, usa expresiones regulares). También se pueden usar expresiones regulares en archie y en la Web; el problema es que la mayoría de las veces usan convenciones diferentes, pero en cualquier caso, nunca viene mal saberlas.
El político corrupto decide no contarle al señor X todo
el dinero obtenido (por no mencionar a Hacienda), y poniéndose a
trabajar sobre los ficheros generados anteriormente (como cliente.mas
),
elabora el siguiente programilla (changec.pl
)
#!/usr/local/bin/perl -p
s/(\d+\.?\d+)/$&*0.85/e;
que ni siquiera pongo en un cuadro aparte, porque no merece la pena.
En este programa se introducen novedades desde la primera línea.
Para empezar, se usa PERL con un switch en la
línea de comandos,
-p
. Este switch indica que alredededor de lo
que hay debajo hay que sobreentender el siguiente bucle
while(<>) { s/(\d+\.?\d+)/$&*0.85/e; print; }
|
; es decir, un bucle que abre y lee del fichero que se pasa en la línea
de comandos, ejecuta las órdenes encontradas en el fichero, y al
final imprime la variable por defecto. Si el switch fuera -n
en vez de -p
no imprimiría.
También se incluye la nueva orden s///[switches]
tomada,
como otras órdenes, del editor de UNIX sed
.
En concreto, esta orden lo que hace es sustituir la expresión regular
encontrada entre los primeros //
por la expresión
en los segundos. En este caso actúa sobre la variable por defecto,
pero en general$zipi="pepeillo";
$zipi=~ s/p/q/g;
daría "qeqeillo".
El switch e
que aparece al final de la orden
en el programa indica que se tiene que evaluar la expresión que
aparece en la segunda parte de la orden; el g
que aparece aquí indica que se tiene que hacer una sustitución
global, es decir, que tienen que sustituirlo todas las veces que
aparezca en la primera expresión, no solo una vez.
Por último, la variable $&
contiene
la cadena que coincida con la expresión regular en la primera parte
de la orden; los paréntesis indican qué parte de la expresión
hay que asignar a tal variable. Si hay varios paréntesis, las cadenas
correspondientes serán asignadas a las "variables" \1,
\2
(dentro de la orden s
), o $1,
$2...
fuera de la expresión (estas variables sólo
funcionan un ratito, asi que no van a estar disponibles hasta que uno quiera;
en caso de duda, es mejor asignarlas inmediatamente a otra variable.
Toda la información sobre expresiones regulares está en la página
de manual perldoc perlre
.
|
[ Pero ya puestos...] [ Preguntas frecuentemente preguntadas] [ Bibliografía.] [ Recursos Internet] [ Presentando lo impresentable] |