[Remembranza] Expresiones Regulares


En muchos lenguajes se usan las expresiones regulares para realizar busquedas, reemplazos e inclusive para determinar si un determinado texto cumple con ciertos parámetros, es una herramienta muy poderosa que nos facilita el trabajo con cadenas de caracteres. Para ejemplificarlo usaré el motor de Expresiones Regulares de Perl.

Supongamos esta frase:

"Aurora Borealis, Lux aeterna luceat eis. Aurora, Aurora, Aurora, Aurora Boreales Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat eis".

Ahora necesitamos hacer "cualquiercosa" sobre la misma, vamos entonces a usar expresiones regulares.

Notación

Para notar que nos referíamos a una ER (expresión regular, desde ahora) usamos, por lo general, una pareja de '/', lo que este adentro será la ER a buscar en el texto. La evaluación de la ER de esta forma nos dirá si la expresión se encuentra o no. Por ejemplo:

Da como resultado Verdadero, pues la frase contiene esa expresión, notese que al encontrarla 1 vez no va a buscar más:

"Aurora Borealis, Lux aeterna luceat eis. Aurora, aurora, aurora, aurora. Boreales Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat eis".

Aunque cabe señalar que para Perl los '/' se pueden reempazar por '[]', '{}' '##', entre otros, inclusive se usa el concepto de 'match' con 'm//' y en medio de los slahs la expresión. Para fines prácticos es lo mismo (a no ser que Perl pueda creer que no es una ER).

Operadores

Así pues, si queremos determinar si una cadena que tiene un contenido variable se encuentra presente se usan un conjunto de operadores.

Operador Punto

Se puede usar el MetaCaracter '.', el cual recoje todo lo que hay en esa posición. Un ejemplo:

Recojeria como cierto tanto "aurora" como "aeterna", pues ambas comienzan y terminan con 'a' y en la mitad hay cualquier cosa (.). Inclusive, aceptaria trozos como Aurora, aurora.

Cabe mencionar que el operador '.' deja pasar el caracter de nueva línea.

Operadores de cuantificación

Así también existen operadores que sirven para evitarnos la repetición de un patrón en una búsqueda, se denominan cuantificadores, están separados en 2, uno que aplica la relación 1..* (el operador +)  y otro que aplica la relación 0..* (el operador *). Lo que significan es la cantidad de veces que se puede encontrar una determinada expresión. Ejemplo:

Aceptaría cualquier expresión en donde "Aurora" y "Borealis" estuviesen separadas por tabulaciones (o por nada, dado que es el operador *), mientras que

Solo aceptaría expresiones donde por lo menos hay una tabulación. Para nuestra frase ejemplo la expresión regular no existe, pues las palabras están separadas por espacios. Si usamos el operador punto

Tendriamos una expresion que acepta encontrar las dos palabras sin importar lo que tengan en el medio (menos, claro está, el caracter de línea nueva). Asi esta expresión tomaría como cierto:

"Aurora Borealis, Lux aeterna luceat eis. Aurora, aurora, aurora, aurora. Boreales Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat eis".

O inclusive la siguiente, si cambiasemos una palabra:
"Aurora Borealis, Lux aeterna luceat eis. Aurora, aurora, aurora, aurora. Borealis Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat eis".

Adicionalmente, en ocasiones queremos encontrar un patrón de repetición determinado, para eso se suelen agrupar las expresiones y decirle a Perl cuantas veces debería buscar. Supongamos que se cometió un error al escribir nuestra frase y tipeamos una 'o' varias veces:

"Aurooora Borealis, Lux aeterna luceat eis. Aurora, aurora, aurora, aurora. Boreales Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat eis".

Entonces podemos usar:

Lo que le dice a Perl que busque un "algo" que se repite 2 veces (aparece 1 y con 2 repeticiones). En este caso: Aurooora. Así mimo la siguiente expresión encontraría la palabra:

Acá en vez de usar un backslash y el número (pues esto implica algunos problemas con la ubicación de los paréntesis, se usa el operador '\g{}' el cual hace la misma función pero más fácil.

Este último operador {} es uno de cuantificación más general que permite determinar la cantidad de veces que podría aparecer una expresión, para el ejemplo anterior me dice que algo (.) aparecera 3 veces. Sin embargo lo podemos usar para expresar intervalos tambien, de manera más general:

En este caso le decimos que la 'o' puede estar entre 1 y 10 veces de manera consecutiva, podemos obviar la cota superior diciendo que puede estar 1 o más veces, o solo poner un valor (p.ej {5}) para decirle que exactamente aparece 5 veces

Así también, si no conocemos la cantidad de veces (si es que hay alguna) que aparece una parte de la ER que buscamos, se usa el operador '?'.

Encontraria tanto 'lu' como 'lux'.

Operadores de Alternativa

Cuando queremos cambiar una palabra u otra independientemente con la ER se usa el operador alternativa, que básicamente es un operador OR.

"Aurora Borealis, Lux aeterna luceat eis. Aurora, aurora, aurora, aurora. Boreales Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat eis".

Este operador encontraría cualquiera de las 2 opciones (por si una de las dos no existiese).

Operador de Clase

En ocasiones buscamos un conjunto de números, letras o una combinación de las mismas, estos conjuntos se denominan clases y se escribe como un rango agrupado, ya puede ser de A-Z, a-z, 0-9, conjuntos más pequeños o combinaciones de todos. Por ejemplo:

Acá la ER busca palabras que comiencen con 'lu' y tenga un conjunto de por lo menos una letra (y sin limite de cuantas) que están entre 'a' y 'z'. De modo que encontraría 'luceat' y 'lux'. Si cambiásemos la ER a

solo encontraria 'luceat' pues la 'x' está por fuera de la clase. Para ser estrictos, el operador '+' y el operador '*' abarcan todo lo que esté despues de 'lu', asi pues podríamos tener toda la frase después del primer 'luceat'.

Así mismo, cuando queremos usar clases completas existen operadores especializados para tal labor:

Por otra parte, si queremos negar la clase usamos un equivalente al operador NOT, el operador '^'.

Modificadores

En ocasiones debemos hacer un poco más o menos estricta la búsqueda de patrones, para eso existen los modificadores:

Que le dice que la expresión no es 'case-sensitive', entonces le vale lo mismo las mayúsculas y minúsculas. Con esta un "Aurora" es igual a un "AURORA" o a un "aUrORa".

Es una modificación del operador punto (.), el cual SI acepta el caracter de nueva línea. En realidad lo que hace es tratar todos los operadores puntos de modo tal que acepten la nueva línea (pero debe haber puntos!!).

Este es un operador que sirve para agregarle espacio a nuestra expresión (para facilitar la lectura, por ejemplo) sin que se altere la expresión. Recordemos que un espacio en blaco equivaldra a uno en la expresión. Asi pues nuestro '//x' lo que hace es facilitar la lectura. Ejemplo:

Nos encontrará todas las palabras que comiencen por 'lu' y esten seguidas por lo menos con una letra o número y eso seguido de cualquier cosa (inclsive nuevas lineas) y termnado en 'is', sin importar si es mayúsculas o minúsculas. Así mismo, los espacios no se tienen en cuenta porque agregamos la 'x'. Sin ese operador sería:

Anclajes

A veces queremos garantizar que nuestra ER corresponda a una palabra en el principio de la cadena, el final de la cadena o que sea una palabra completa y no un segmento, para esto se usan anclajes.

El operador '^' en este caso no es negación, es un anclaje para asegurar que la palabra este al principio de la cadena, que no haya nada antes.

"Aurora Borealis, Lux aeterna luceat eis. Aurora, Aurora, Aurora, Aurora Boreales Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat eis".

Pero si existiese la palabra "AAurora" no la aceptaría, porque rompe el anclaje.

El operador '$' es el opuesto al anclaje anterior, asegura que después de la palabra no habrá más.

"Aurora Borealis, Lux aeterna luceat eis. Aurora, Aurora, Aurora, Aurora Boreales Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat".

Así mismo si estuviese las palabra "luceat eis" no entraría en el patrón.

Existen anclajes para ponerle fronteras a las palabras sin importar su ubicación dentro del texto, en este caso '\b', la cual le pone frontera a la palabra en alguno de los extremos, así mismo '\B' es la negación y significa "sin fronteras".

Estas ER no se encontraria en nuestra frase pues buscan "alis[cualquiercosa]" o "[cualquiercosa]auro". Así mismo, si queremos encontrar "Borealis" como palabra completa sería:

Así mismo:

Buscaría una palabra que comience por "Boreal" y después sigue cualquier cosa (sin frontera), como "Borealis" o "Boreales".

Variables en Perl

A veces nos interesa no solo determinar si una ER está en la cadena que estamos analizando, sino que deseamos extraer un contenido de a cadena, así pues podemos almacenar esos valores en unas variables especiales.

Asignación

Para encontrar el resultado (booleano) de  la evaluación de nuestra ER usamos el operador '=~'

En este caso, la expresión sería evaluada como 'true' dado que en nuestra frase está la ER.

Variables

Para extraer texto de nuestra expresión regular Perl nos ofrece la posibilidad de expresar qué deseamos sacar, esto se realiza metiendo la expresión a extraer en unos paréntesis, y automáticamente Perl lo almacena en las variables especiales $1, $2, $3...

"Aurora Borealis, Lux aeterna luceat eis. Aurora, Aurora, Aurora, Aurora Boreales Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat eis".

Luego, como sólo hay un par de paréntesis, sólo se usará la variable $1, y ahi quedará guardado "eis". Para

Las variables serán: $1="Lux", $2="aeterna", $3="eis". Son 4 palabras (no espacios ;)) separadas por espacios y confinadas entre una coma y un punto, sin importar mayúsculas o minúsculas.

Cabe señalar que esas variables especiales sólo existen después de cada evaluación. A veces tenemos demasiadas variables al punto que es dificil hacerle seguimiento a cada una, entonces podemos usar el hash especial de Perl '%+', al cual le podemos indicar las claves mediante el operador '?':

En este caso, las variables se accesan como: $+{primera}="Lux", $+{segunda}="aeterna" y $+{tercera}="eis".

En ocasiones necesitamos paréntesis para organizar un poco nuestra ER, pero entonces ¿cómo hacemos para que esos paréntesis no los guarden las variables? Sencillo, solo trucamos un poco los paréntesis usando '?:':

Esta expresión buscaría lo mismo, solo que no usaría 3 variables especiales sino sólo 2: $1="Lux" y $2="eis", dado que el espacio correspondiente a "aeterna" esta agrupado con los paréntesis truncados.

Perl también nos da la facilidad de usar sus 3 variables especiales ($&, $` y $') para acceder a la ER, a lo que está antes y lo que está después (respectivamente). Si nuestra frase fuese únicamente:

"Aurora Boreales, Lux aeterna."

Y la ER:

Al correrla se buscaría una palabra que estuviese después de un espacio y antes de una coma, luego las variables serán: $&=" Boreales,", $`="Aurora" y $'=" Lux aeterna.", nótese los espacios y las comas/puntos.

Procesamiento de Textos

Si queremos cambiar una expresión por algo en especial se usa un tipo operador mas complejo: la substitución 's///':

Al correrla sobre nuestra frase obtendremos:

"aeterna Borealis, Lux aeterna luceat eis. Aurora, Aurora, Aurora, Aurora Boreales Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat eis".

Sin embargo no se cambiaron todas las "Auroras" sino solo la primera. Esto sucede porque la expresion se busca hasta que algo la cumple, sin importar si en el resto del texto hay más expresiones que se cumplan. Para eso existe el reemplazo global '/g':

Y obtendremos:

"aeterna Borealis, Lux aeterna luceat eis. aeterna, aeterna, aeterna, aeterna Boreales Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat eis".

La substitución nos facilita también el cambio de letras por mayúsculas o minúsculas según se necesite, los operadores para eso son:

 \UCambia toda la expresión a mayúsculas.
 \LCambia toda la expresión a minúsculas.
 \uCambia la letra que le sigue a mayúscula.
 \lCambia la letra que le sigue a minúscula.
 \EEs la letra de escape, termina el formato.

Luego de esas 4 sustituciones tendremos:

"AURORA borealis, lux Aeterna luceat eis. AURORA, AURORA, AURORA, AURORA Boreales lux Aeterna, lux Aeterna, lux Aeterna luceat eis. lux Aeterna luceat eis".

Donde pasará todas las "Auroras" a mayúsculas, las "Lux" a minúsculas, le retirara la mayúscula a "Borealis" (pero no a "Boreales") y le pondrá a todas las "aeternas" la primera letra mayúscula.

Operadores especiales

Existen también un conjunto de operadores que interactuan con ER y variables de Perl de manera tal que puedan complementarse.

Split

Este operador revisa una cadena y saca todo lo que este por fuera de la ER y lo guarda en un arreglo, donde cada lemnto separado por la ER es un elemento del arreglo.

En este caso tomara todos los elementos de una cadena y los separa donde encuentre la ER 'expr', y almacenara cada uno en una posición de arreglo. P.ej una IPv6 la convertiria:

Luego

Join

Este operador podríamos nombrarlo como el inverso a split, este toma los elementos de un arreglo y los junta en una cadena con algún caracter o palabra definida en la ER.

Así pues, nuestra IPv6 quedaría:

Luego

Avaricia

Los operadores de las ER suelen ser muy avaros al momento de evaluar las expresiones, recordemos nuestra frase:

"Aurora Borealis, Lux aeterna luceat eis. Aurora, Aurora, Aurora, Aurora Boreales Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat eis"

Si usaramos los operadores '*' o '+' ellos no solo tomaran lo que nosotrs pensamos, sino que intentaran tomar absolutamente todo, y luego el evaluador de la expresión debera devolverse. Por ejemplo:

Podria hacer:

"Aurora Borealis, _aeterna_ eis. Aurora, Aurora, Aurora, Aurora Boreales Lux aeterna, lux aeterna, lux aeterna luceat eis. Lux aeterna luceat eis"

Como también:

"Aurora Borealis, _aeterna_ eis"

Ó:

"Aurora Borealis, _aeterna_ eis. Lux aeterna luceat eis"

Así entonces aparecen los operadores que no son an invasivos '*?' y '+?', que hacen lo mismo, pero son minimalistas, solo tomaran como valida la expresión más pequeña que encuentre.