La primera duda que se nos plantea es: ¿Qué es una expresión regular? Intentare explicarlo con mis palabras. Una expresión regular es una cadena que hace patrón de construcción de otras cadenas, es decir, algo que nos indica que forma debe tener una cadena.
La principal manera de usar una expresión regular es compararla directamente con la cadena, y decimos que la expresión regular "casa" con la cadena cuando dentro de la cadena, encontramos al menos una subcadena que coincida con el patrón. La subcadena puede ser un fragmento de la cadena o la cadena al completo.
De esta manera podemos construir una expresión regular que "case" con palabras que comiencen por a luego tengan una serie de b's y acaben por c y asi detectar tanto la palabra 'abc' como la palabra 'abbbbc' y sus infinitas combinaciones.
Este documento ha sido desarrollado por Iván Arias basandose en la documentación de las expresiones regulares PCRE en www.php.net.
El documento ha sido realizado para www.php-hispano.net. Si tienes alguna duda de las expresiones regulares o de PHP en general, visita la página o chatea con nosotros en #php_para_torpes del iRC-Hispano.
Para realmente adquirir algún conocimiento de este documento es recomendable que leas el documento por orden. Según avanza el documento la dificultad es creciente, con lo que si no comprendes algún punto, no sigas leyendo, revisa los apartados anteriores y asegúrate de que comprendes todos los ejemplos.
También sería conveniente que según vayan surgiendo los ejemplos, los pruebes y hagas variaciones sobre ellos para asegurarte que realmente comprendes lo que se trata de explicar. Este documento puede ayudar a comprender las expresiones regulares, pero si no trabajas tú con ellas y no pruebas cosas, es muy dificil que aprendas a usarlas.
Me gustaría que me hicieseis llegar comentarios acerca del documento, qué os pareció, en que partes tuvisteis más dificultad... Así podré ver que partes son más dificiles de entender y tratar de explicarlas de una manera más detallada.
La mayoría de los caracteres suelen casar consigo mismo, es decir, la expresión regular "a" casaría con cualquier 'a' de la cadena. Sin embargo, para lo construcción de la expresión regular necesitamos ciertos caracteres especiales que nos hagan, en cierto modo, de "modificadores" (por ejemplo, para indicar que un carácter se puede repetir un cierto número de veces)
A continuación os detallo estos caracteres especiales y su significado. Si necesitásemos casar con uno de estos caracteres en el texto, tendríamos que poner delante el carácter de escape.
Si el carácter siguiente a la barra es un carácter no alfanumérico, esta elimina cualquier significado que este pudiera tener, es decir, si necesitas capturar en el texto '*' o '(' necesitarías poner "\* y "\(" respectivamente.
En otros lenguajes de programación, la cadena "\(" daría un error de carácter de escape no válido. Esto es debido a que despues de '\', busca un carácter de escape (como puede ser el carácter de salto de linea '\n' o el caracter de tabulación '\t'), y al encontrarse '(' genera un error dado que no es un carácter de escape válido. La construcción adecuada sería escapando '\', con lo que quedaría "\\(".
PHP en este sentido es un poco más listo, si el carácter que está a continuación del carácter de escape no es un cáracter de escape válido, interpreta '\' como barra de texto. De esta manera, la cadena "\(" y la cadena "\\(", son iguales.
Estos son los principales caracteres de escape:
Dentro de las expresiones regulares, hay otros caracteres de escapes que definen tipos genéricos de caracteres:
Estos caracteres son muy útiles a la hora de construir las expresiones regulares, cuando necesitemos casar hasta un espacio, siempre es mejor usar el caracter '\s' que poner el espacio (a no ser que sea extrictamente el espacio). Usar estos caractees aporta mayor flexibilidad a nuestras espresiones regulares.
Las clases de caracteres sirven para definir rangos o grupos de caracteres. Por poner un ejemplo, si definimos la siguiente clase de caracteres "[aeiou]" casaría con las vocales de la cadena. Cada clase de caracteres casa únicamente con uno de los caracteres de la cadena, es decir, si compramos "[aeiou]" con "oue", la expresión regular sólo casaría con la 'o'. A las clases de caracteres les podemos aplicar los caracteres cuantificadores que vimos al principio, de esta manera "[aeiou]+" si que casaría con la cadena entera.
De los caracteres que vimos al principio, dentro de una clase de caracteres no todos son válidos ni tienen el mismo significado, estos son los caracteres que podemos usar:
Si necesitáramos usar alguno de estos caracteres dentro de la clase, es necesario que los escapemos (tambien '[' y ']'), sin embargo ya no es necesario escapar el resto de caracteres especiales que vimos al comienzo del documento ('$', '+' o '(' por ejemplo no necesitarian ser escapados).
Los caracteres que definen tipos genéricos de caracteres que vimos en el apartado anterior, también pueden ser usados dentro de una clase de caracteres.
Ejemplos:
Más adelante explicaré como hacer que una expresión regular como "[a-z]+" no diferencie entre mayúsculas y minúsculas.
Los subpatrones están delimitados por paréntesis y pueden estar anidados, es decir, puede haber un subpatrón dentro de otro subpatrón. Lo subpatrones tiene principalmente 2 utilidades:
La primera de ellas es agrupar para usar el carácter de rama alternativa '|'. Por ejemplo la expresión regular "mace(donia|ta)" casaría tanto con la cadena "macedonia" como con la cadena "maceta". De quitar los paréntesis ("macedonia|ta") casaría con las palabras "macedonia" y "ta".
La segunda de ellas es para capturar fragmentos a los que podremos acceder usando '\' o almacenarlos para su posterior uso (por ejemplo, la función preg_match de PHP tiene un parámetro opcional en el que almacenaría los grupos capturados).
Pongamos que queremos capturar todas las "cadenas" dentro de otra cadena, es decir, queremos buscar dentro de la cadena unas comillas, luego una sucesión de caracteres y luego otras comillas de cierre. Construimos la expresión regular paso a paso:
Ya tenemos la expresión regular partida en cachos. Si los juntamos todos obtenemos la expresión regular que andabamos buscando: "\".*\"". Ahora para marear un poco más la perdiz, supongamos que ademas de las cadenas que usan comillas dobles, tambien queremos que nuestra expresión regular case con las cadenas que usan comillas simples.
Cuando en una expresión regular decimos que necesitamos casar con "esto" y con "esto otro", lo mas seguro es que necesitemos usar el caracter de inicio de rama alternativa '|'. Necesitamos que nuestra expresión regular detecte tanto comillas dobles como comillas simples, asi que en vez de usar "\"", se nos ocurre poner "\"|\'" y lo introducimos: "\"|\'.*\"|\'"
Al principio vimos que hay que tener cuidado al usar el carácter '|' porque si no agrupamos bien puede ser que la expresión regular no funcione como queremos. En este caso, lo que pretendiamos hacer, está mal. Esa expresión regular, necesitamos usar un subpatron para agrupar las comillas. "(\"|\').*(\"|\')" si haría lo que intentabamos conseguir.
Pero si nos fijamos en un detalle de la expresión regular anterior, podemos darnos cuenta de que no funciona de una manera adecuada, primero podría casar unas comillas dobles y para cerrar unas comillas simles, o viceversa, por lo que esta expresión regular no sirve.
Entoces se nos ocurre contruir la expresión regular " \".*\" | \'.*\' " (Los espacios están puestos para hacerla mas legible, para que funcione debemos quitarlos) y si conseguimos que case con cadenas de comillas simples y cadenas de comillas dobles. El problema de esta expresión regular es que, si necesitasemos añadir algún otro caracter (pongamos que queremos capturar desde un '#' hasta otro) tendríamos que añadir "|#.*#" y así con cualquier carácter que necesitemos.
Es en este momento en el que nos preguntamos si podríamos decir a la expresión regular que encuentre un carácter de los que delimitan lo que queremos casar, luego la sucesión de caracteres y que al final encuentre el mismo caracter que encontró al principio.
La respuesta es que si, para comenzar necesitamos capturar ese carácter que andabamos buscando, para ello usamos un subpatrón: "(\"|\'|#|...)" (Los puntos suspensivos indican que podrías añadir cualquier caracter que necesites). A continuación ponemos la sucesión de caracteres ".*". Y ahora necesitamos decirle a la expresión regular, usa el carácter que capturamos al principio, en el primer subpatrón.
Cuando estudiamos el carácter de escape '\', mencionamos algo de "referencia anterior". Usando este caracter y a continuación poniendo el número del subpatrón capturado (se cuentan de izquierda a derecha), accedemos al valor de captura del mismo. Es decir, "\1" es el valor del primer subpatrón capturado, "\2" del segudo...
Un vez sabemos estos, para contruir la expresión regular sólo juntamos los cachos que explique antes, y obtenemos: "(\"|\'|#|...).*\1", la expresión que andábamos buscando, y de una manera optimizada.
Al comienzo del documento se detallaron los caracteres especiales y, en el caso de los cuantificadores, explique de una manera muy breve su funcionamiento. Para entenderlos mejor voy a explicar como funcionan los caracteres de inicio y fin de valores mínimo máximo.
El uso de este cuantificador es el siguiente: "X{min, max}", donde X puede ser un carácter, una clase de caracteres o un subpatrón, y 'min' y 'max' el número de veces mínimo y máximo que aparece X. Por ejemplo "\w{2,4}" indica palabras de dos a cuatro letras. Existen dos construcciones alternativas:
El resto de caracteres cuantificadores puede definirse en base a estas construcciones:
Las expresiones regulares por defecto son codiciosas. Para explicar lo que es la codicia usaré la expresión regular "(\"|\').*\1" usada de ejemplo para los subpatrones.
Si comparamos la cadena "<a href='index.php' target='_self'>" con la expresión regular anterior, '.*' intentará coincidir con todos los caracteres posibles. En este caso la expresión regular casaría con "'index.php' target='_self'", es decir, desde la primera comilla hasta la última comilla simple, esto ocurre porque la expresión regular es codiciosa.
Una solución a este problema puede ser utilizar una clase de caracteres. Si cambiamos '.*' por '[^\1]+', especificamos que en la expresión regular no puede haber una comilla igual que la primera en la cadena (recordar que \1 hacia referencia a la comilla capturada). La expresión regular resultante sería: "(\"|\')[^\1]*\1".
Aunque ahora haría perfectamente lo que necesitamos, más adelante veremos que no podemos usar esa clase de caracteres. Como adelanto diré que, haremos que nuestra expresión regular acepte cadenas con comillas escapas, es decir, si tenemos ""hola \" que tal"", la comilla del medio es una comilla escapada, y nuestra expresión regular deberia detectarlo e incluirla dentro de la cadena, si usamos la clase de caracteres "[^\1]+" no podríamos conseguirlo ya que especificamos que dentro de de la cadena no queremos ninguna comilla.
La solución correcta es cambiar la codiciosa de la expresión regular . Una expresión regular no codiciosa trata de casar siempre con el menor número de caracteres posible. Por ejemplo la expresión regular ".+" no codiciosa siempre casaría sólo con un único caracter. Si cambiasemos la codicia de la expresión regular "(\"|\').*\1", ".*" trataría de casar con el menor número de caracteres, con lo que la expresión regular casaría desde la primera comilla, hasta la siguiente.
Ahora la pregunta lógica que se nos plantes es: ¿y cómo cambiamos la codicia de una expresión regular. A esta pregunta a mi se me ocurren tres posibles respuestas.
La primera de ellas es usar el carácter '?' el cual, al lado de un cuantificador, invierte la codicia del mismo, es decir, usando ".*?" en vez de ".*". La expresión regular "(\"|\').*?\1" casaría con "'index.php'", que es lo que andabamos buscando.
La segunda y la tercera opción tienen que ver con un tipo de subpatrones especiales y modificadores respectivamente, más adelante explicaremos estas otras opciones.
Las funciones tienen un comportamiento predeterminado que puede ser cambiado mediante el uso de modificadores. A continuación pongo una lista de los principales modificadores y su efecto.
Los delimitadores de patrón son caracteres que indican el comienzo y el fin de la expresión regular, en PHP el más extendido es '/'. A lo largo del documento he ido poniendo expresiones regulares que, si las probasteis en PHP, no funcionen por la falta de estos mismos.
En cuantificación y codicia llegamos a la expresión regular "(\"|\').*?\1". Para que esta funcione correctamente en PHP debemos ponerle los delimitadores de patrón, la expresión regular introduciendo los delimitadores sería "/(\"|\').+?\1/".
Los modificadores se introducen después del cierre del delimitador. Podríamos hacer la misma expresión regular manteniendo la codicia de esta manera "/(\"|\').*\1/U". Si ahora ponemos el carácter '?' al cuantificador, la expresión volvería a ser codiciosa, dado que con el modificador hacemos que el patrón no sea codicioso, y al aplicar '?' modificamos la codicia del cuantificador volviendo a hacerlo codicioso.
Ejemplos:
Los subpatrones especiales se distinguen porque llevan el carácter '?' después del paréntesis de apertura "(? … )". Hay varios tipos de subpatrones especiales. Ninguno de estos tipos de subpatrones es capturado.
Cuando con anterioridad hablábamos del uso de subpatrones para agrupar, poníamos este ejemplo de expresión regular "mace(donia|ta)". Ahora que sabemos un poco más (o eso espero), vemos que estamos capturando el subpatrón y que la verdad no nos vale para nada. Los subpatrones de no captura sirven para cuando solo queremos agrupar y no capturar.
Se construyen poniendo "?:" después del paréntesis. De esta manera, si construimos la expresión regular "mace(?:donia|ta)", la referencia "\1" no existe.
El uso primordial de estos subpatrones es no crear referencias inútiles y optimizar la memoria, por ejemplo, si construimos una expresión regular con muchas capturas que no necesitamos, a la hora de casar la expresión regular con la cadena, se hará un uso de memoria innecesario. Si además usamos un preg_match_all, todas estas capturas las introducirá en un array, siendo el consumo de la memoria mucho mayor.
Los subpatrones modificadores sirven para activar o desactivar modificadores a la expresión regular, se construyen de la siguiente manera "(?x-y)", donde 'x' son los modificadores que queremos activar y 'y' los modificadores que queremos desactivar. No tienen que estar ambos presentes. Si no queremos desactivar ningún modificador no se debe poner '-'.
Al desactivar un modificador, la función vuelve a tomar su estado por defecto, es decir, si activamos 'U' la expresión regular deja de ser codiciosa, si a continuación lo desactivamos, la expresión regular vuelve a ser codiciosa.
Un ejemplo aclarativo, podría ser "(?iU)[a-z]+(?s-U).+(?-i)[a-z]+". Primero activamos 'i' y 'U', luego activamos 's' y desactivamos 'U', y por ultimo desactivamos el modificador 'i'. El ejemplo no tiene ningún propósito salvo el mostrar la construcción de este tipo de subpatrones.
Seguimos trabajando con la expresión regular "(\"|\').*?\1" que obtuvimos en Cuantificación y Codicia. Pongamos otro ejemplo, que el usuario tiene una cadena semejante a esta "<libro titulo='Jack\'s dog'>". En la cadena podemos ver que la persona que construyó ese supuesto código XML, escapó con '\' las comillas simples para indicar que no es comilla de fin de cadena, sino una comilla que pertenece a la cadena. Nuestra expresión regular no funcionaría como sería correcto y sólo capturaría "'Jack\'".
Lookbehind sirve para buscar ocurrencias detrás de la expresión, lookahead para buscar delante. Es este caso podríamos usar lookbehind para detectar si detrás de las comillas hay una '\'. La construcción de estas expresiones es la siguiente:
Para nuestro problema necesitamos casar con una comilla que no esté precedida de una barra, mirando las construcciones anteriores vemos que la que mejor se ajusta a nuestro problema es "(?<!x)y".
'y' ya lo tenemos, es la comilla, para nuestra expresión regular "\1". A lo hora de construir 'x' puede presentarsenos algún problema, si ponemos "(?<!\\)" la expresión regular dará error. PHP interpreta las dos barras como una sola, y la expresión regular al detectar una barra y un paréntesis detecta ese paréntesis como escapado. La construcción correcta es "(?<!\\\\)".
Juntando estas dos partes e introduciendolas en nuestra expresión regular, obtenemos "(\"|\').*?(?<!\\\\)\1", la expresión regular que andabamos buscando.
El funcionamiento de lookahead es similar y no creo que sea necesario explicarlo.
Existen dos formas posibles de subpatrones condicionales:
(?(condición)patrón-si)
(?(condición)patrón-si|patrón-no)
Para empezar se evalúa la condición, si es positiva, la cadena se compara con el 'patrón-si', si resulta negativa se compararía con el 'patrón-no' (en caso de haberlo).
La condición puede ser de dos tipos, una referencia a un grupo de captura anterior o una aserción lookahead. Pondré un ejemplo de cada para aclarar un poco el funcionamiento.
Para el ejemplo de uso de un subpatrón condicional, usando como condición una referencia a un subpatrón capturado, voy a volver a la expresión regular "(\"|\').+ (?<!\\\\)\1" a la cual venimos dando vueltas desde el principio del documento.
Con un patrón condicional podríamos realizar la misma expresión regular de esta manera: "(\"|\')?(?(1).+?(?<<\\\\)\1)". Con "(\"|\')?" miramos que exista una comilla en la cadena, acto seguido con "(?(1).+?(?<!\\\\)\1)" comprobamos si se ha encontrado, en caso afirmativo se ejecuta el patrón ".+?(?<!\\\\)\1" en el que comprobamos que halla una secuencia de caracteres y una comilla para cerrar igual que la que se abrió sin estar escapada.
Para el ejemplo con la aserción lookahead voy a utilizar otro ejemplo, dado que no podría detectar que se abra y cierre el mimo tipo de comillas. Supongamos que en una cadena queremos capturar todo lo que hay entre paréntesis (si, se que se parece), empezare construyendo la expresión regular poco a poco. La expresión lookahead seria "(?=\()", y queremos que si encuentra eso, busque una sucesión de caracteres, y al final un paréntesis de cierre, para lo que utilizaremos ".+\)"
Juntando las partes que tenemos, la expresión regular resultante sería "(?(?=\().+\))".
Las principales funciones de PHP para trabajar con expresiones regulares PCRE son las que se llaman preg_*. A continuación doy una breve explicación de cómo funcionan, no voy entrar en detalles, muchas funciones tienen más parámetros opcionales que no voy ni a mencionar.