2008/02/14

Expresiones reguleras

El otro día un conocido tenía un problemilla con las expresiones regulares y me ofrecí a ayudar. Lo dejo por aquí para no olvidarlo (que es lo que tienen estas cosas, que uno se olvida de ellas frecuentemente).

Las expresiones regulares son un mecanismo la mar de potente para buscar texto. Es tanta la potencia que tienen que normalmente no usamos más que lo básico y al necesitar algo más avanzado podemos perdernos (por eso existen algunas herramientas para hacer y comprobar las expresiones regulares, aunque personalmente yo para eso uso el editor Vim). Como introducciones a las expresiones regulares hay en Internet a miles, no me extenderé en eso y paso al problema en cuestión, que consistía en buscar con PHP algunas agujas de texto en un pajar de HTML.

En el pajar se reconocen 3 tipos de agujas que nos interesa encontrar:

<!-- textoA textoB-->
<!-- textoA textoB str1-->
<!-- textoA textoB str1 str2-->

De ahí interesa diferenciar los 3 casos y extraer str1 y str2 en los 2 últimos casos. El código PHP con las expresiones regulares que había al principio era más o menos este (pongo sólo las funciones preg_*, que es lo interesante):

preg_match("/<!-- textoA textoB-->/", $pajar);
preg_match_all("/<!-- textoA textoB (.+)-->/", $pajar, $arr);
preg_match_all("/<!-- textoA textoB (.+) (.+)-->/", $pajar, $arr);

El problema aquí es que la segunda expresión encuentra las 2 últimas agujas, por lo que la tercera expresión no servía de nada (por que estaba en un else que dependía de la segunda). Una vez resuelto puede parecer evidente, pero a simple vista tampoco se me ocurrió a mi lo que estaba pasando.

El problema es el punto: el punto hace coincidir cualquier carácter, y el espacio en blanco también es un caracter, por lo tanto es normal que aparezcan casos de la tercera aguja cuando sólo nos interesa la segunda. La solución pasa por buscar cualquier carácter excepto el espacio que tiene que haber entre str1 y str2 (asumí que ni str1 ni str2 van a contener ningún espacio, pero es una presunción peligrosa). La forma de decir esto mediante expresiones regulares es mediante un conjunto de exclusión (que es igual que el resto de conjuntos pero el primer carácter debe ser un acento circunflejo '^'). Las expresiones quedan así:

preg_match("/<!-- textoA textoB-->/", $pajar);
preg_match_all("/<!-- textoA textoB ([^ ]+)-->/", $pajar, $arr);
preg_match_all("/<!-- textoA textoB ([^ ]+) ([^ ]+)-->/", $pajar, $arr);

Como he comentado antes, si por algún casual la str1 o la str2 que nos interesan tuviesen espacios, ni la segunda ni la tercera expresión regular encontrarían nada, lo que puede ser un problema; pero nada que no se arregle con unos cuantos modificadores en las expresiones :)

Y esto ha sido todo en la entrada de San Valentin (es lo que tiene amar a las expresiones regulares) XD

2 comentarios:

  1. Para buscar palabras yo creo que queda más claro el \w que equivale a [a-zA-Z0-9].

    ...textoA textoB (\w+) (\w+)...

    aunque bueno, si metieras simbolos raros, lo de buscar cualquier cosa menos espacio sería mejor :)

    ResponderEliminar
  2. Hombre, el guión '-', el espacio subrayado '_', y los números [0-9], son caracteres que no son tan raros (especialmente si lo que se quiere recoger son nombres de funciones, variables o cosas así).

    ResponderEliminar

Nota: solo los miembros de este blog pueden publicar comentarios.