http://www.merelo.net/tutoperl/tutoperl10.html
Expresiones
regulares: comparación y sustitución
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
letra {letra|número}*
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).
|
Tabla 4: operadores de expresiones
regulares |
|||||||||||||||||||||
|
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;
}
|
Tabla 5: Switches o
interruptores que se pueden usar en la línea de comandos |
|||||||||||
|
; 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.
|