Expresiones regulares
Muchos editores y procesadores de textos ofrecen la posibilidad de cambiar una cadena de caracteres por otra. Supongamos que hemos escrito sistemáticamente mv
entre vocales en lugar de nv
: fácilmente sustituiríamos lo primero por lo segundo, por ejemplo comvenir
por convenir
. En español aplicaríamos esta sustitución a todas las palabras, y de un golpe de tecla habríamos terminado.
A veces se nos ofrece además imponer la restricción de que la secuencia de caracteres sea una palabra entera. Por ejemplo cambiar magia
por majia
para dar un toque Nueva Era. Entonces, ¡atención!, la sustitución habrá tenido en cuenta el contexto.
Las expresiones regulares hacen esto y mucho más. No sólo reconocen secuencias de caracteres y palabras enteras, sino también principios de palabra o de línea y en general cualquier esquema. Por ejemplo podríamos buscar todas las palabras que terminan en y
utilizando la marca de límite de palabra (\b
), así: y\b
. Más adelante explicaremos todos los componentes que encontramos en un esquema, intervalos de caracteres ([a-z]
), opciones (este|ese
), etc.
Además las expresiones regulares nos permiten seleccionar una parte de la secuencia de caracteres encontrada (captura) y utilizarla en la sustitución. Tal vez esto no resulte muy útil en textos redactados
pero piénsese en una lista de nombres en la que el nombre de pila apareza al final después de una coma, como es usual, y deseáramos quitar la coma y ponerlo al principio. Querríamos cambiar Domínguez Herrera, Laura
por Laura Domínguez Herrera
etc. Entonces tendríamos que capturar los apellidos y el nombre y escribir el nombre seguido de los apellidos, sin la coma, claro.
En general las expresiones regulares resultan muy útiles para tratar todo tipo de textos que estructuren la información según un formato. Cumplimos así uno de los objetivos principales de la informática: automatizar las tareas mecánicas. Las aplicaciones irán saliendo solas si manejamos este tipo de textos. En lo que sigue voy a explicar tanto los esquemas como la forma de capturar trozos de cadenas para ponerlas en el texto final.
Abreviaturas
En los esquemas con los que seleccionamos cadenas de caracteres utilizamos las llamadas abreviaturas. Van todas precedidas de una barra hacia atrás que evita que la letra que sigue se interprete literalmente. Si queremos referirnos a una barra inclinada hacia atrás, entonces la escribimos dos veces: \\
. Nada más hay que explicar. La lista de abreviaturas es:
-
El punto (.) se corresponde con cualquier carácter, pero entre corchetes se interpreta literalmente:
[.]
se corresponde con un punto -
^
se corresponde con el principio de una línea -
$
se corresponde con el final de una línea -
\xxx
se corresponde con un valor hexadecimal -
\uxxx
se corresponde con un valor de Unicode -
\b
se corresponde con un límite de palabra -
\B
se corresponde con un no límite de palabra -
\c
se corresponde con unretorno de carro
-
\d
se corresponde con un dígito -
\D
se corresponde con un no dígito -
\f
se corresponde con unform feed
-
\s
se corresponde con un espacio -
\t
se corresponde con un tabulador -
\w
se corresponde con una letra o dígito (word) -
\W
se corresponde con lo que no sea ni letra ni dígito
Obsérvese que la mayúscula es la negación de la minúscula.
Conjuntos de caracteres y disyunciones
En lugar de un caracter podemos poner un conjunto de caracteres de los cuales puede aparecer uno cualquiera en la selección. La regla es que ha de ir encerrado entre corchetes. Por ejemplo, cualquier vocal no acentuada se representaría mediante [aeiou]
. También podemos establecer intervalos separando los extremos mediante un guión. Todas las minúsculas se encajaría mediante [a-z]
. Y todas las letras, minúsculas o mayúsculas, mediante [a-zA-Z]
.
Si entre corchetes aparece el sombrero (^
), ninguno de los caracteres que le siguen pueden aparecer. [^0]
selecciona cualquier carácter que no sea el cero, y [^ ]
selecciona cualquiera que no sea el espacio.
Podemos especificar varias opciones separándolas mediante barra vertical (|
), que significa o
. Una manera alternativa de especificar todas las letras sería por tanto: [a-z]|[A-Z]
. Si especificamos más de dos opciones, intercalamos la barra vertical entre cada par, como en duque|rey|reina
, sin añadir espacios de separación.
Cuantificadores
Los elementos de un esquema que no sean literales llevan a la derecha una indicación de cuantas veces puede aparecer. El cuantificador más elemental consiste en un número entre llaves o un mínimo y un máximo separados por coma entre llaves. Por ejemplo, [aeiou]{2}
-donde el cuantificador es {2}
, por supuesto- encajaría con cualquier vocal doble, y [aeiou]{1,2}
encajaría con cualquier vocal simple o doble.
En lugar de números entre llaves se pueden usar: ? (cero o uno), + (uno o más), y * (cualquier cantidad, incluído cero).
Ejemplos de búsqueda
-
[^.;:\(\)][ ][A-Z][a-z]
busca palabras que empiezan por mayúscula pero cuya segunda letra es minúscula y no siguen un punto y seguido, punto y coma, dos puntos ni paréntesis (lo que nos sirve para ver cuántos sustantivos comunes hemos resaltado con mayúscula); -
ao\b
busca palabras terminadas enao
; -
\b[^ ]{7}\b
busca palabras de 7 letras (o caracteres), incluídae2porta
; -
\\|\(|\)
busca barras verticales así como apertura y cierre de paréntesis, todos los cuales ha habido que resguardar con barra inclinada; -
\b[^ aeiou][aeiou]\b
busca palabras (\b
) bisílabas compuestas de una no vocal seguida de una vocal (incluídas secuencias erróneas como_a
); -
delante (d[^e]|[^d])
busca todas las veces que la palabradelante
no va seguida de la secuenciade
, y hemos tenido que poner los miembros de la disyunción entre paréntesis para forzar a que se interprete así; -
desde [ w]* hasta [ w]*
busca secuenciasdesde ... hasta ...
sin signos de puntuación entre medias dado que sólo permite letras (\w
) y espacios;
Capturas
Se capturan
grupos de caracteres para repetirlos en el texto final. Esto se hace encerrando entre paréntesis las secuencias que nos interesan. Se pueden hacer varias capturas. Las capturas se utilizan o recuperan poniendo en la secuencia de sustitución una barra inclinada seguida de un número, empezando por el 1 (\1, \2
etc.).
Para comprenderlo resolveré el problema propuesto al principio de la lista de nombres en la que el nombre de pila aparecía al final después de coma. Supongamos además que cada nombre ocupa una línea. Tenemos que escribir dos cadenas:
-
una cadena de búsqueda con dos capturas, la de los apellidos sin la coma y la del nombre:
^([^,]*), (.*)
; -
una cadena de sustitución:
\2 \1
.
En general se cumple que la cadena de sustitución es lo más fácil. Otros ejemplos de captura con sustitución serían:
-
(d{2,3})[-]?(d{6,7})
captura las dos partes de un número de teléfono con el prefijo (entre 2 y 3 dígitos) separado del resto (6 ó 7 dígitos) por un guión opcional, todo lo cual podemos sustituir por el número de teléfono sin guiones (\1\2
); -
trans([^aeiouáéíóú])
buscatrans
seguido de consonante, la cual captura, que podríamos sustituir portras
seguido de la consonante, mediantetras\1
;
Pero para apreciar el gran potencial de las expresiones regulares con sustitución desarrollamos a continuación una serie de ejemplos de tratamiento de textos de HTML y XML.
Captura de mitades en las que partir una palabra
Las siguientes expresiones regulares sirven para capturar dos grupos entre los que se desea partir una palabra insertando la secuencia <breakable/>
y llevan todas la substitución \1\2
:
-
(^[^<]*[áéíóú])([bcdfghjklmnprstvwxyz][áéíóúaeiouü])
, para tratar la secuencia vocal acentuada, consonante y vocal acentuada o no, análoga de... -
(^[^<]*[aeiou])([bcdfghjklmnprstvwxyz][áéíóúü])
; -
(^[^<]*[aeiouáéíóú])(ñ)
para la eñe; -
([aeiouáéíóú])(gü)
, -
(^[^<]*([^<]*n)(s[bcdfghjklmnprstvwxyz])
divideins-piración
, pero sólo en la primera línea de una cadena de caracteres entre paréntesis;
Otras expresiones regulares útiles son:
-
([^ ]<[^<]>)
se substituye por por\1
para intercambiar espacio y orden en XML, -
^([^{]*(.*)[ ]*linkdest)
captura la declaración de un destino para encerrarlo entre(<) pop
y(>) pop
a fin de evitar que se inserten marcas de ruptura de palabra por estar supuestamente dentro o constituir parte de una orden XML.