La primera vez que ves una de ellas sientes nauseas. A partir de ese momento cada vez que te enfrentas a ellas sientes mareos, hasta que poco a poco empiezas a entender su idioma. Llega un día en el que te sientes capaz de dialogar con ellas. La amistad se ha forjado.

Con las expresiones regulares ocurre como con los frameworks de desarrollo. La curva de aprendizaje suele ser lenta, pero siempre acaba mereciendo la pena. Son las encargadas de poner orden en los caos con los que muchas veces tienen que lidiar nuestros pobres procesos.

Gracias a ellas podemos confeccionar laboriosas expresiones como por ejemplo “Quiero saber cuales son todas las palabras que preceden a todos los números de 4 cifras que no terminan en 2″. Inventate una condición, seguro que se puede construir.

AVISO: No he sometido a prueba ninguno de los ejemplos que aquí pongo.

En PHP, como en la mayoría de lenguajes, es muy sencillo construirlas. En su forma más básica:

<?php
    $pattern = '/dd/';
    $subject = "El día 22 me voy de viaje";
    preg_match($pattern, $subject, $match);

    var_dump($match);
?>

Este artículo no pretende ser una guía de iniciación a las expresiones regulares (Hay libros enteros que tratan sobre ello). Si estás interesado, hay páginas muy útiles, aunque personalmente me entiendo mejor con la nueva guía que hay disponible en la documentación de PHP.

Quiero apuntarme, a modo de recordatorio, varias trucos muy útiles que he aprendido recientemente.

Asignar nombres a subpatrones

Cuando ejecutamos una expresión regular, podemos capturar todos los subpatrones que se han detectado para usarlos más adelante. Por ejemplo, si parseamos una fecha en formato ISO-8601 con esta expresión regular “/(dddd)-(dd)-(dd)/”, obtendremos un array en el que en la posición 0 tenemos el año, en la 1 el mes y en la 2 el día. ¿Y si un día introducimos un nuevo subpatrón en medio de los anteriores? Bien, la solución pasa por asignar nombres:

"/(?P<nombre>d{4})/"

Ahora tendremos ese valor en $array['nombre']. ¡Muy útil!

No capturar un subpatrón

¿Y si tenemos un subpatrón del que no nos interesa realmente su valor? La memoria es limitada, y para no llenarla innecesariamente utilizaremos esto:

"/(?:d{4})/"

Repeticiones perezosas

A mendo queremos que un patrón se detenga tan pronto como se haya cumplido la condición que busca (Este no es el comportamiento por defecto… Las expresiones regulares son voraces). Por ejemplo, supongamos que queremos obtener el nombre “Julia” de este texto:

<p>Julia</p><p>Álvaro</p>

Esta expresión regular no nos serviría: “/<p>(.+)</p>/”, ya que capturaría esto: “Julia</p><p>Álvaro

La solución fácil es esta (Nota el símbolo de interrogación):

"/<p>(.+?)</p>/"

La difícil, o la que yo venía usando hasta ahora era esta, que capturaba todos los caracteres excepto el que delimitaba el fin de la cadena buscada:

"/<p>([^<]+)</p>/"

Back references

En lenguajes como XML, gramáticas independientes del contexto, necesitamos hacer referencias a elementos anteriores. Por ejemplo

<miEtiqueta>Un valor <b>cualquiera</b></miEtiqueta>

¿Cómo podemos parsear eso si no conocemos de antemano el nombre de la etiqueta?

"/<(.*?)>(.*?)</1>/"

Por supuesto, también podemos usar el nombre del subpatrón si lo tuviera:

"/<(?P<tagname>.*?)>(.*?)</(?P=tagname)>/"

Aserciones

Una maravilla… Básicamente son condiciones que no mueven el cursor, es decir, no consumen ningún caracter. Hay de dos tipos:

  1. Búsqueda por delante. A su vez dividida en dos subtipos:
    1. Positiva. Debe cumplirse la condición: (?=patron)
    2. Negativa. No debe cumplirse la condición: (?!patron)
  2. Búsqueda por detrás. A su vez dividida en dos subtipos:
    1. Positiva. Debe cumplirse la condición: (?<=patron)
    2. Negativa. No debe cumplirse la condición: (?<!patron)

Por ejemplo, esta expresión regular:

"/(?<!foo)bar/"

Encuentra aquellas ocurrencias de “bar” que no están precedidas por “foo”. En este caso hemos usado búsqueda por detrás negativa.

Una gran pega es que las condiciones que hay dentro de las aserciones deben tener un tamaño de caracteres fijo. En el caso de PHP y de Java, se permite además tener una serie de palabras opcionales, cada una de tamaño fijo. La única excepción notable es .NET framework, que sí permite “lo que sea” (¿A que no te lo esperabas?).

En fin, que igual igual alguno de estos “trucos” le es útil a alguien. A mi se me aparecen situaciones donde usarlos todas las semanas. ¡Disfrutadlos!