Expresiones Regulares en PHP


Última Actualización: 31/08/2022 19:03 Nro de Vistas: 11110

Expresiones Regulares en PHP

¿Qué es una expresión regular?

phpUna expresión regular en programación es una cadena en la que se indica un patrón para construir otras cadenas; en una expresión regular se dice la forma que ha de tener una cadena.

La forma más común de uso de una expresión regular es su comparación con una cadena para determinar si dicha cadena, o una subcadena de ella, sigue el patrón especificado en la expresión regular.

Por ejemplo podemos construir una expresión regular con un patrón que sea una "c" seguida de una "a", después cualquier combinación de letras y que termine por "a". Con esta expresión regular detectaríamos por ejemplo la palabra casa, caja, camioneta y cualquier otra palabra que comience por "ca" y termine por "a".


Una expresión regular está formada por los caracteres que buscamos y por meta-caracteres. Los meta-caracteres son caracteres especiales que se interpretan de una forma especial y no se representan a ellos mismos.

Caracteres especiales (meta-caracteres) usados en las expresiones regulares

En una expresión regular, cualquier carácter coincide consigo mismso, es decir, la expresión regular "c" sería un patrón que coincidiría con cualquier c de una cadena. Por ello se reservan algunos caracteres con un significado especial. Por ejemplo un cierto meta-carácter que veremos más adelante puede indicar que la c que buscamos se repita una sola vez o que se repita más veces.

Estos meta-caracteres y lo que hacen se detallan a continuación:

  • \ - Es el carácter de escape. Veremos su funcionamiento en detalle más adelante. Por ahora decir que si necesitamos buscar este o cualquier otro carácter especial (que no se representan a sí mismo) en una cadena, tenemos que poner el carácter de escape antes del carácter especial que deseamos buscar. Por ejemplo, el carácter "(" es un meta-carácter (que veremos más adelante) y para buscarlo la expresión regular sería "\(".
  • ^ - Carácter de inicio de cadena. Indica que los caracteres que buscamos deben estar al inicio de la cadena. Por ejemplo, la expresión regular "f" coincidiría con cualquier 'f' de la cadena; la expresión regular "^f" solo será verificada si la cadena en la que buscamos tiene una f al principio. El meta-carácter '^' coincide con una posición, no con un carácter.
  • $ - Carácter de final de cadena. De forma análoga al punto anterior, la expresión regular "f$" será validada si la cadena con la que se compara tiene una f al final.
  • . (punto) - el punto coincide con cualquier carácter. Es decir, si tenemos que buscar una f, seguida de cualquier carácter, y una t, escribiríamos la siguiente expresión regular: "f.t". Ojo: el punto coincide con cualquier carácter, pero solo uno, la expresión regular 'f.t' coincidiría con fot, fat, pero no con foit ni foot ni faat, etc.
  • [ - Este es el carácter que indica el inicio de la definición de una clase de caracteres. Veremos más adelante como se usa.
  • ] - Con este carácter indicamos el fin de la definición de una clase de caracteres.
  • | - Este carácter tiene una función lógica y su significado es "o", es decir, si necesitamos buscar una f o una g (los dos caracteres son válidos) escribiríamos "f|g".
  • ( - El paréntesis izquierdo indica el comienzo de un subpatrón (lo veremos más adelante).
  • ) - Indica el final de un subpatrón.
  • * - Este es un carácter cuantificador cuyo significado es cero o más. Por ejemplo, "f*" es una expresión regular que se validaría con una cadena vacía (cero) pero también con la cadena "f", con la cadena "ff", con la cadena "fff", .....
  • + - Este es otro modificador cuantificador cuyo significado es uno o más. Se puede decir que es igual que "*" con la excepción de que no valida con una cadena vacía.
  • { - Este modificador indica el inicio de valores mínimo máximo. Lo explicaremos mas adelante.
  • } - Indican el fin de valores mínimo máximo.
  • ? - Modificador de usos múltiples: cuantificador, el llamado modificador de codicia y también sirve para indicar la definición de subpatrones especiales. Iremos viendo su uso a lo largo del artículo.

Veamos con un poco más de detalle alguno de los meta-caracteres utilizados en expresiones regulares vistos en los puntos anteriores.

Carácter de escape

El carácter de escape "\" indica que el siguiente carácter no se va a utilizar en la búsqueda si es un carácter alfanumérico. Pero si el carácter siguiente a la barra no es un carácter alfanumérico (lease meta-carácter), la barra elimina cualquier significado que pueda tener. Por ejemplo, el carácter "(" es, como vimos, un meta-carácter; si necesitamos buscar "(" deberemos poner "\(" para que se elimine su comportamiento de meta-carácter y se pueda buscar en la cadena. En algunos lenguajes de programación es necesario escapar también la barra si el carácter que sigue a la barra no es un carácter de escape válido (que veremos a continuación), por ejemplo, "\(" tendría que escribirse en estos lenguajes como "\\(". En php no ocurre esto ya que php interpreta "\" como barra de texto si el carácter siguiente no es un carácter de escape válido, por tanto "\(" y "\\(" tendrían el mismo significado si utilizamos php como lenguaje de programación.

Ejemplo:

Si la cadena es "Tengo en mi bolsillo 40$", la expresión regular "40$" no da coincidencia ya que la expresión regular indica que buscamos la cadena "40" al final de la cadena y la cadena no termina en "40" sino que termina en "40$". La expresión regular correcta para indicar que buscamos 40$, sin importar la posición, indicaríamos "40\$" (escapamos el meta-caracter $).

Algunos conjuntos de caracteres formados con el carácter de escape "\" tienen un significado especial o representan a un grupo de caracteres; de ellos estos son los más importantes:

  • \n - Salto de línea
  • \r - Retorno
  • \t - Tabulador
  • \xhh - Carácter de código hexadecimal. hh puede ser una letra comprendida entre la "a" y la "f" sin diferencia mayúsculas y minúsculas.
  • \ddd - Carácter de código octal o de referencia anterior. Aunque aún no lo hemos visto, en las expresiones regulares se pueden capturar subpatrones, un uso de carácter es hacer referencia a esas cadenas capturadas. De no haber estas capturas será interpretado como un número octal.

Otros caracteres especiales que representan a un grupo de caracteres y pueden ser de utilidad son:

  • \d - Cualquier dígito decimal. Comprende cualquier dígito del 0 al 9. También abarca los códigos ASCII 178, 179 y 185 (los superíndices 2, 3 y 1 respectivamente).
  • \D - Cualquier carácter que no esté incluido en el punto anterior, es decir, cualquier carácter no decimal.
  • \s - Coincide con un carácter espaciador, por ejemplo ' ', '\t', '\n' o '\r'.
  • \S - Coincide con cualquier carácter no espaciador
  • \w - Cualquier número o letra (carácter alfanumérico). Incluye también el '_' ("guión bajo"). En otras palabras, \w coincide con los llamados caracteres de palabra.
  • \W - Cualquier carácter no incluido en \w, es decir, cualquier carácter no alfanumérico ni '_'.
  • \b - Este metacaracter coincide con los llamados caracteres de fin de palabra. Un carácter fin de palabra se encuentra siempre que se tenga un carácter \w y seguidamente un \W.
  • \B - Lo contrario a \b, es decir, cualquier carácter que no sea carácter fin de palabra.
  • \a - Es practicamente igual que '^' y coincide con el llamado carácter de inicio de cadena.
  • \A - Carácter de inicio de línea. A diferencia de \a o de ^, el meta-carácter \A coincide tanto al principio de la cadena como al principio de cada nueva línea.
  • \z - Prácticamente igual a '$, carácter de fin de cadena.
  • \Z - Carácter de fin de línea. A diferencia de \z o de $, \Z coincide con el final de la cadena y con el final de cada línea.

Puedes preguntar el por qué de usar estos metacaracteres, por ejemplo ¿por qué usar \s para indicar un espacio si puedo poner directamente el espacio? Como respuesta decir que un espacio en blanco puede ser eso, un espacio en blanco, pero también puede ser otro carácter como \t, \n o \r y, por tanto, poner directamente el espacio podría no dar el resultado esperado (a no ser busques estrictamente el espacio).

Clases de caracteres [ ]

Una clase de caracteres define un conjunto de caracteres. Podemos definir nuestro propio conjunto de caracteres introduciéndoles entre dos corchetes []. Por ejemplo, la clase "[tyui]" indicará coincidencia si se encuentra en la cadena con una "t", "y", "u" o una "i". Es importante decir que si tenemos varios caracteres seguidos que coinciden con la clase, solo habrá coincidencia con el primero de la cadena. Por ejemplo, si comparamos la cadena "yi" con la expresión regular "[tyui]", sólo habrá coincidencia con la "y"; para que haya coincidencia con la cadena completa podríamos usar "[tyui]+".

Dentro de una clase los metacaracteres que hemos visto anteriormente no tienen el mismo uso y, por tanto, no es necesario escaparlos. Es decir, si buscamos el carácter $, no tenemos que escribir "\$". Esto es debido a que dentro de una clase, se toman los caracteres del código ASCII, esto hace también que no sea igual una letra mayúscula que su análoga en minúscula. Se exceptúan de esta característica los siguientes caracteres especiales para clases:

  • \ - Carácter de escape general.
  • - - Carácter de rango. El guión se usa para construir rangos de caracteres, por ejemplo, "[a-z]" incluye todo el rango de caracteres desde la "a" a la "z", incluidos los extremos del rango. "[A-Z]" sería igual pero en mayúsculas. He de mencionar otra vez que se usa la tabla de caracteres ASCII, por ello rangos como "[*-}]" son válidos.
  • ^ - Carácter utilizado para indicar los caracteres que NO buscamos. Por ejemplo, la clase "[0-9^85] coincidirá con cualquier dígito del 0 la 9 excepto el 8 y el 5.

Si necesitamos buscar alguno de los tres caracteres expuestos en los tres puntos anteriores deberemos escaparlos. También hay que escapar los corchetes ('[' y ']'). Por ejemplo, la expresión regular "[\\a\^]" define el conjunto de caracteres compuesto por '\', 'a' y '^'.

Dentro de una clase de caracteres se pueden usar los conjuntos de caracteres que vimos más arriba como '\d', '\w', etc.

Ejemplos:

  • "[a-Z]+" - Si buscamos que una expresión regular coincida con mayúsculas y minúsculas esta clase de caracteres sería errónea pues en el código ASCII el carácter 'a' es mayor que el carácter 'Z' y, por tanto, el rango a-Z no es un rango válido.
  • "[A-z]+" - Esta expresión serviría para lo anterior pero con el problema de que hay 6 caracteres entre la 'Z' y la 'a' ('[', '\', ']', '^', '_' y '`') que también darían positivo.
  • "[a-zA-Z]+" Esta expresión sería una solución para el problema expuesto en los dos puntos anteriores pero no resultaría efectiva si buscamos en el texto caracteres no recogidos en la tabla ASCII y que forman parte del vocabulario de algunos idiomas como la ñ o vocales con tilde en el idioma castellano/español.
  • "\w+" - Con esta expresión solucionaríamos todos los problemas anteriores, es insensible a minúsculas y mayúsculas y además incluye caracteres como la ñ o vocales con tilde.

Subpatrones ()

Los subpatrones, también conocidos como grupos anónimos, son una expresión regular 'anidada' en un patrón u otro subpatrón. Tienen una utilidad indiscutible, entre ellas:

  • Agrupar para usar el carácter de rama alternativa '|' (es como "esto 'o' esto"). Por ejemplo la expresión regular "pelo(ta|na)" daría resultado si la comparamos con pelota o con pelona.
  • Capturar fragmentos de la cadena. Con esta captura podemos acceder posteriormente a estos fragmentos de muy fácil y escribiendo muy poco código. Además, hay funciones, como la función preg_match de php, que tiene la opción de recoger en un array los fragmentos capturados pudiendo ser usados posteriormente en el script php.

Ejemplo:

Queremos buscar la cadena (sea cual sea su contenido) comprendida entre dos caracteres, por ejemplo, desde unas comillas dobles a otras comillas dobles. La expresión regular podría ser "\".*\"" (en php es necesario escapar las comillas ya que las usa como delimitadores del valor de variables). Si ahora se nos plantea que las comillas dobles pueden ser sustituidas por comilla simple y también sería válido para lo que buscamos, la expresión regular sería "\"|\'.*\"|\'." Podemos hacer uso de los subpatrones para agrupar partes de la expresión y capturarlas; haciendo esto la expresión anterior quedaría mucho más corta de esta forma: "(\"|\').*\1". El subpatrón (\"|\') queda capturado y hacemos referencia a él con el número 1; el segundo subpatrón que apareciese en la expresión se capturaría con el número 2, y así sucesivamente. Nota que el número de referencia a un subpatrón capturado hay que escaparlo.

Cuantificación y codicia

La codicia en la expresiones regulares es un concepto que puede ser complicado de entender. Vamos a explicarlo junto con el cuantificador de valores mínimo y máximo.

El cuantificador mínimo - máximo tiene la forma "X{min,max}". X puede ser un carácter, una clase de caracteres o un subpatrón. min indica el número de veces, como mínimo, que queremos que aparezca X; de forma análoga, max indica el número de veces que, como máximo, queremos que aparezca X.

A tener en cuenta en el cuantificador mínimo máximo:

  • El valor max puede omitirse, sin omitir la coma, para indicar el valor mínimo y no limitar las coincidencias con un valor máximo. Por ejemplo X{2,} indica que X debe aparecer 2 veces o más (un mínimo de 2 y sin máximo).
  • Si se omite el valor max y también la coma, por ejemplo X{2}, se indica que X debe aparecer exactamente el valor introducido, en este caso X debe aparecer exactamente 2 veces.

Ejemplo:

  • La expresión regular "\w{5,}" indicará que buscamos palabras de 5 o más letras (cualquier carácter de palabra - w -  que se repite 5 veces o más).
  • La expresión regular "\w{5,10}" indicaría que buscamos palabras de entre 5 y 10 letras.
  • La expresión regular "\w{5}" nos dice que buscamos palabras de, exactamente, 5 letras.

Otros cuantificadores y su relación con el cuantificador mínimo máximo

Se puede decir que los demás cuantificadores usados en expresiones regulares son abreviaciones de un cuantificador mínimo máximo.

  • X* sería igual que X{0,}: X se debe encontrar cero o más veces.
  • X+ sería igual que X{1,}: X debe aparecer una o más veces.
  • X? corresponde a X{0,1}: X debe aparecer cero o una vez, es decir, puede aparecer una vez o puede no aparecer.

Intentemos ir entrando en el concepto de codicia. Las expresiones regulares son, por defecto, codiciosas. Esto significa que siempre intentará coincidir con el máximo número de caracteres posible.

Por ejemplo, podemos tener el código de un link (elemento html < a >) en el que aparece href y title. Los valores de ambos atributos del link se definen con comillas. Supongamos que tenemos la siguiente cadena:

< a href="http://www.undominio.com" title="Visitame" >Mi web< /a >

Si comparamos esta cadena con la expresión regular que contruimos anteriormente "(\"|\').*\1", la expresión regular coincidirá con "http://www.undominio.com" title="Visitame". En otras palabras, la expresión regular, al ser codiciosa, y en su afán de intentar coincidir con el máximo número de caracteres, coincidirá con todos los caracteres encontrados entre la primera comilla y la última comilla, situación que es válida según la expresión regular construida.

¿Que pasa si queremos que la expresión regular solo coincida con los caracteres situados entre la primera comilla y la segunda? La solución es modificar la expresión regular para que no sea codiciosa. Una expresión regular no codiciosa hará justamente lo contrario a una expresión codiciosa, es decir, intentará coincidir con el menor número posible de caracteres que se ajusten al patrón dado. Para hacer que una expresión regular no sea codiciosa tenemos tres posibilidades.

  • Uso del modificador ?: El meta-carácter ? es, como vimos, un cuantificador y también un modificador. Si ponemos ? junto a un carácter, clase de caracteres o subpatrón, actuará como cuantificador (0 o 1). Si ponemos ? junto a otro cuantificador, actuará como modificador invirtiendo la codicia del mismo. Por tanto, la expresión regular "(\"|\').*\1" para que no sea codiciosa quedaría "(\"|\').*?\1"
  • Las otras dos posibilidades de cambiar la codicia de expresiones regulares es el uso de subpatrones especiales y otros modificadores que veremos más adelante.

Modificadores en expresiones regulares

Los modificadores son meta-caracteres que "modifican" el comportamiento predeterminado de la expresión regular. Los principales modificadores son:

  • m - Modificador multilínea. Los meta-caracteres '^' y '$' coinciden con la posición de comienzo o final de la cadena respectivamente; con el modificador 'm' se consigue que coincidan también con el final o el comienzo de cada línea, es decir, se hace que el carácter de inicio de cadena '^' se comporte como '\A' y el que el carácter de de fin de cadena '$' se comporte como '\Z'.
  • s - Como vimos anteriormente, el punto '.' coincide con cualquier carácter excepto el carácter de salto de línea '\n'. Con el modificador 's' se consigue el el punto coincida también con el salto de línea '\n'.
  • i - (i latina) Este modificador hace que la expresión regular no distinga entre minúsculas y mayúsculas.
  • x - El modificador 'x' hace que se ignore cualquier espacio en blanco de la expresión regular y que no haya sido escapado. También hace que se ignore cualquier carácter no escapado que esté después del '#' (esto se usa principalmente para introducir comentarios del programador).
  • e - Modificador que solo funciona en la función preg_replace de php y hace que la expresión regular modificada sea interpretada como código php.
  • U - Modificador que invierte la codicia de la expresión regular.
  • A - Modificador de anclaje; hace que el patrón solo de positivo si se encuentra al principio de la cadena.
  • X - Hace que la compilación de error si existe el carácter '\' y no va seguido de un carácter de escape. Por ejemplo '\j' daría error, habría que poner '\\j'.

Delimitadores de patrón

Los delimitadores de patrón son los meta-caracteres que indican el inicio y el final de la expresión regular. Son obligatorios en expresiones regulares que utilicen la sintaxis Perl como los regex de las funciones php preg_match, preg_match_all, preg_replace, y todas las funciones php tipo preg_. En el lenguaje php el delimitador de patrón más usado es '/' aunque hay que tener en cuenta que es válido cualquier carácter no alfanumérico y que si dentro de la expresión regular se busca el mismo carácter que el usado como delimitador de patrón, este tendrá que ser escapado (vea un ejemplo más adelante).

En el post hay ejemplos que al usarlos en php no funcionarán si no se introducen los delimitadores de patrón. Por ejemplo, el patrón"(\"|\').*?\1" para usarlo en php quedaría "/(\"|\').*?\1/" introduciendo los delimitadores de patrón.

Si queremos usar un modificador, hay que ponerlo tras el cierre del delimitador. Así, llegamos a la expresión"/(\"|\').*?\1/U" como otra de las formas de invertir la codicia de la expresión que comentábamos cuando explicamos la cuantificación y codicia.

Algunos ejemplos:

  • "/[a-z]+/U" - buscaría una cadena compuesta por letras de la 'a' hasta la 'z' sin diferenciar entre minúsculas y mayúsculas.
  • "/\(.+\)/U" - este patrón coincidirá con la cadena compuesta por un paréntesis, cualquier carácter que no sea salto de línea y otro paréntesis.
  • "/\(.+\)/Us" - Este patrón buscará lo mismo que el anterior pero sin importar que haya saltos de línea entre los paréntesis.
  • "/http:\/\/www\.bloogie\.es\//": buscamos la url de este sitio. Nota como se ha utilizado la barra (forward slash) como delimitador de patrón y, por tanto, la barra tiene que ser escapada en el interior del regex con una barra invertida (backslash). Se podría haber utilizado otro carácter como delimitador de patrón, por ejemplo %, y en este caso no se tendría que escapar la barra en el interior del regex: "%http://www\.bloogie\.es/%".

Subpatrones especiales

Los subpatrones especiales se construyen igual que los subpatrones normales pero se diferencian por llevar un '?' justo tras el paréntesis de apertura: "(?.....)". Los subpatrones especiales no son capturados. Veamos los subpatrones especiales más destacados:

Subpatrón "no capturar"

El subpatrón de no captura se construye de la forma "(?: ... )". Este subpatrón no se capturará. Se utiliza para construir un subpatrón que no necesitamos que se capture, de este modo, mejoramos el rendimiento y uso de la memoria de la máquina. La programación no es sólo hacer que nuestro código haga lo que queramos, también hay que optimizar recursos.

Por ejemplo, tenemos la expresión regular "pelo(ta|na)", el subpatrón (ta|na) es capturado y la referencia "\1" es almacenada en la memoria. Si no vamos a usar este subpatrón más en nuestra expresión y, por tanto, no necesitamos esta referencia, usaremos "pelo(?:ta|na)".

Subpatrones modificadores

Estos subpatrones activan unos modificadores para la expresión y desactivan otros. Son de la forma "(?x-y)" donde 'x' son los modificadores a activar e 'y' son los modificadores que se quieren desactivar. También se puede usar solo para activar o solo para desactivar. "(?U)" activará el modificador 'U' sin desactivar ninguno (se omite el guión y el segundo parámetro); "(?-U)" desactivará 'U' sin activar ningún modificador (se omite el primer parámetro pero no el guión).

Si tenemos una expresión con un modificador activado y luego lo desactivamos, la expresión regular vuelve a su estado por defecto; por ejemplo, si activamos el modificador 'U' la expresión regular dejar ser codiciosa (estado por defecto) y si luego desactivamos el modificador 'U' la expresión regular vuelve a ser codiciosa ('U').

Ejemplo:

"(?iU)[a-z]+(?s-U).+(?-i)[a-z]+" Se activan los modificador 'i' y 'U', luego se activa 's' y se desactiva 'U' y luego se desactiva 'i'.

Subpatrones de búsqueda detrás/delante: lookbehind y lookahead

Los subpatrones lookahead y lookbehind se usan para buscar un carácter o subpatrón justo antes o después de otro carácter o subpatrón:

  • (?
  • (?
  • y(?=x) Busca 'y' sólo si va seguido de 'x'.
  • y(?!x) Busca 'y' sólo si no va seguido de 'x' (o fin de cadena).

Si tenemos este supuesto código XML

De forma análoga se construyen las expresiones regulares con subpatrones lookahead.

Los subpatrones condicionales en expresiones regulares

Los subpatrones condicionales es una forma de decir "si se cumple tal condición, comparar la cadena con este patrón y (opcional) si no se cumple dicha condición comparar la cadena con este otro patrón".

De esta forma, las dos posibles construcciones de los subpatrones condicionales con:

  • (?(condicion)patron-true): si se cumple la condición se compara la cadena con el patron-true.
  • (?(condicion)patron-true|patron-false): si se cumple la condición se compara la cadena con el patron-true, si no se cumple la condición la cadena es comparada con el patron-false.

Como condición se puede poner la referencia a un subpatrón capturado anteriormente o un subpatrón lookahead. Veamos un ejemplo. Supongamos que queremos capturar la fecha dada en una cadena de texto en la forma "Fecha: dd-mm-yyyy" dónde dd es el día en número (dos dígitos), mm es el mes en número (dos dígitos) e yyyy es el año en número (cuatro dígitos). Para sacar la fecha de una cadena de ese tipo podemos utilizar:

"/^Fecha:\s(\d{2})-(\d+{2})-(\d{4})$/"

Ahora bien, imaginemos que tenemos nuestra web también en inglés y que en la versión en inglés la fecha la ponemos como "Date: dd, mmm yyyy" Dónde dd es día en número (2 dígitos), mmm es una abreviatura de tres letras del nombre del mes e yyyy es el año en número (4 dígitos). Entonces podríamos hacer la siguiente expresión regular con un patrón condicional (fíjate como la condición es un patrón lookahead):

"/(?(?=^Date)Date:\s(\d{2}),\s([A-Za-z]{3})\s(\d{4})$|Fecha:\s(\d{2})-(\d{2})-(\d{4})$)/" 

Con esta expresión regular decimos que si se cumple la condición de que la cadena de texto comience con la palabra "Date" se utilice el patrón "Date:\s(\d{2}),\s([A-Za-z]{3})\s(\d{4})$", en caso contrario se usará "Fecha:\s(\d{2})-(\d{2})-(\d{4})$".



Fuente: http://www.bloogie.es/tecnologia/programacion/58-como-construir-expresiones-regulares-en-php#ixzz28NfIB9Oa