Introducción a las expresiones regulares en PHP

Las expresiones regulares permiten encontrar similitudes con un patrón alfanumérico.

matisse

archivado en: PHP/AJAX / 16 marzo, 2013

Hace tiempo que tenía ganas de escribir una entrada sobre expresiones regulares en PHP, que una vez controladas son realmente útiles. Para ir viendo cómo funcionan emplearé la función preg_replace($patron, $reemplazo, $cadena), que reemplaza las coincidencias de un patrón, es decir, de una expresión regular, en una cadena.

Así, por ejemplo, reemplazaríamos todas las aes de la cadena por una R.

$cadena = "Soy una cadena con 123 números y guiones <>";

$patron = "/a/";

$reemplazo = "R";

echo preg_replace($patron, $reemplazo, $cadena);

// esto da como resultado: Soy unR cRdenR con 123 números y guiones <>

Delimitadores de patrón

El patrón, en inglés pattern, debe estar delimitado por unos caracteres especiales que le indiquen a PHP donde empieza y donde termina. Se puede emplear «cualquier carácter no alfanumérico, que no sea una barra invertida, y que no sea un espacio en blanco», pero lo más frecuente es emplear una barra inclinada //. Lo que esté dentro de estos dos signos es el «patrón», es decir, los caracteres que deben coincidir (macht, en argot).

Si necesitamos que en patrón haya precisamente una barra, se puede «escapar» con la barra invertida. (Se entiende por escapar que PHP no debe leer las características especiales de ese signo, sino tratarlo como uno más).

$cadena = "Soy una cadena con /barras/";

$patron = "/\//";

$reemplazo = "R";

echo preg_replace($patron, $reemplazo, $cadena);

// Resultado: Soy una cadena con RbarrasR

Modificadores del delimitador

La manera de comportarse del patrón puede modificarse añadiendo después del delimitador final determinados caracteres. Un ejemplo sencillo: los patrones son por lo general case sensitive, es decir, que se diferencia entre mayúsculas y minúsculas. Por ejemplo, de esta manera solo coincidirían las aes mayúsculas.

$cadena = "Iba Antonio a América a andar a gatas";

$patron = "/A/";

$reemplazo = "R";

echo preg_replace($patron, $reemplazo, $cadena);

// Resultado: Iba Rntonio a Rmérica a andar a gatas

Pero añadiendo el modificador i (PCRE_CASELESS) al delimitador del patrón podemos forzarlo para que se comporte de manera case insensitive.

$patron = "/A/i";

// Resultado: IbR Rntonio R RméricR R RndRr R gRtRs

Metacaracteres

La gracia de los patrones es que permiten incluir caracteres comodín, metacaracteres en phpesiano, que permiten afinar la búsqueda. Por ejemplo, el acento circunflejo ^ indica que la coincidencia debe encontrarse al principio de la cadena. Así, en este caso, no se produciría ningún reemplazo, ya que la cadena empieza por S.

$cadena = "Soy una cadena con 123 números y guiones <>";

$cadena = "Soy una cadena antigua";

$patron = "/^a/";

$reemplazo = "R";

echo preg_replace($patron, $reemplazo, $cadena);

En cambio así quitaríamos la S.

$patron = "/^S/";

// Resultado: Roy una cadena antigua

Lo mismo sucede con el símbolo de dólar o peso $ para indicar que debe estar al final de la cadena. Fijaos que en este caso el carácter a encontrar va antes del patrón.

$cadena = "Soy una cadena en paz";

$patron = "/z$/";

 // Resultado: Soy una cadena en paR

Los metacaracteres principales son:

Carácter  Coincidencia Cadena Resultado
. (/./) Cualquier resultado abcd12345 RRRRRRRRR
 ^ (/^a/) Principio de la cadena abcd12345 Rbcd12345
 $ (/5$/) Final de la cadena abcd12345 abcd1234R
\s (/\s/) Espacios en blanco ab 345 abR345
\d (/\d/) Cualquier número abcd12345 abcdRRRRR
\D (/\D/) Cualquier letra abcd12345 RRRR12345
\w (/\w/) Cualquier letra, número o guion bajo a*d_12^!5 R*RRRR^!R
\W (/\W/) Cualquier carácter que no sea una letra, un número o o el guion bajo a*d_12^!5 aRd_12RR5

Cuantificadores

Mediante el uso de llaves podemos indicar cuántas veces debe aparecer como mínimo y máximo el patrón. Las llaves se escriben después del match y, con el primer número, se indica el mínimo de veces que debe aparecer y, en el segundo, el máximo. Por ejemplo, esto:

$cadena = "a aaa aaa aaaa";

$patron = "/a{2,3}/";

$reemplazo = "R";

Daría como resultado: a R R Ra. Es decir, en el primer grupo de aes no encuentra nada, pues debe hacer como mínimo 2; y en el cuarto solo reemplaza tres, pues debe haber como  máximo 3.

Si no se indica el segundo número y no se pone la coma, el macht debe aparecer exactamente ese número de veces.

$patron = "/a{2}/";

// Resultado: a Ra Ra RR

Y si se pone la coma, pero no el segundo número, se entiende que el máximo es ilimitado (bueno, inferior a 65536, que para el caso es lo mismo).

$patron = "/a{2}/";

// Resultado: a Ra Ra RR

Existen tres caracteres comodines para definir cuantificadores: *, + y ?

  • *: equivale a {0,}
  • +: equivale a {1,}
  • ?: equivale a {0,1}

Por ejemplo, así nos cambiaría todas las aes porque al menos se encuentran 1 vez:

$cadena = "a aaa aaa aaaa";

$patron = "/a+/";

// Resultado: R R R R

Un poco más adelante comprenderemos su utilidad con algunos ejemplos prácticos, pero sigamos ahora con la teoría para conocer los subpatrones ^^.

Subpatrones

Mediante corchetes [] y paréntesis () podemos crear subpatrones, que sirven para encontrar expresiones regulares dentro de otras. Los corchetes se para comparar rangos. Por ejemplo, con el rango a-z encontraríamos cualquier carácter en minúscula comprendido entre la a y la z. Así, en este patrón no encontraríamos ni la primera e, pues está precedida por una T mayúsucula, ni la segunda, que está precedida por un número.

$cadena = "Tengo un mero somero y tú una pera llamada 1e";

$patron = "/[a-z]e/";

// Resultado: Tengo un Rro soRro y tú una Rra llamada 1e

 

Si el rango va precedido por un acento circunflejo se produce justo el efecto contrario, es decir, se seleccionan las coincidencias (letra e precedida por cualquier carácter) que no cumplen la condición.

$patron = "/[^a-z]e/";

// Resultado: Rngo un mero somero y tú una pera llamada R

Rangos habituales:

  • [a-z]: cualquier carácter en minúscula.
  • [A-Z]: cualquier carácter en mayúscula.
  • [0-9]: cualquier número.
  • [[:alpha:]]: cualquier carácter del alfabeto.
  • [[:digit:]]: cualquier número.
  • [[:punct:]]: cualquier carácter que no sean números y letras (como una exclamación ! o una coma ,).
  • [[:space:]]: cualquier espacio en blanco.
  • [[:upper:]]: cualquier mayúscula.
  • [[:lower:]]: cualquier minúscula.

Los paréntesis sirven para crear subexpresiones y buscar alternativas mediante el operador |, que significa o (o esto o lo otro). Por ejemplo, esto capturaría ma, pero también ca.

$cadena = "Roma no cayó en un día";

$patron = "/(m|c)a/";

// Resultado: RoR no Ryó en un día

Un ejemplo

Para terminar este toxo-post vamos a ver un ejemplo que nos permita comprender las posibilidades de las expresiones regulares. Pero antes hablaré de otra función muy útil para estos casos:  preg_match(), que busca una coincidencia en un patrón, en cuyo caso devuelve 1 (true, verdadero); de lo contrario, 0 (false). Y ahora al lío ^^.

Las expresiones son muy útiles para validar los datos de un formulario. Por ejemplo, sabemos que cualquier correo electrónico debe tener esta estructura:

cadena1@cadena2.cadena3

Es decir, debe cumplir estas condiciones:

  • Antes de la arroba debe haber una cadena de texto.
  • Luego tiene que haber una arroba.
  • A continuación otra cadena de texto.
  • Un punto.
  • Y luego otra cadena que debe tener al menos dos caracteres.

¿Nos animamos a intentarlo antes de ver la solución?

Una solución posible sería esta:

$patron = "/([a-z]|[0-9])+@([a-z]|[0-9])+\.[a-z]{2,}/i";

if ( preg_match($patron, $cadena) == 0 ) {

echo "Correo inválido";

} else {

echo "Correo válido";

}

Cadenas que funcionarían con ese patrón:

  • $cadena = "zutanito@gmail.com";
  • $cadena = "zutaas2223ito@gmail.com";

Cadenas que no funcionarían con ese patrón:

  • $cadena = "zutanitogmail.com";
  • $cadena = "@gmail.com";
  • $cadena = "zutanito@.com";
  • $cadena = "zutanito@gmail.";
  • $cadena = "zutanito@gmailcom";

Además de validar formularios, otra utilidad entre tantas de las expresiones regulares es conseguir URLs amigables, pero eso lo vemos en otro post que por hoy ya está bien. Para los impacientes, dos enlaces muy chulos: al blog de Juan Díaz Bustamante y a Desarrollo Web, dos páginas de consulta obligada para quien gusta de las cosas de Internet.

Epílogo: POSIX y PCRE

En PHP hay había dos maneras de trabajar con expresiones regulares. Una es siguiendo el modelo POSIX, acrónimo de Portable Operating System Interface, y otra el modelo PCRE, de Perl Compatible Regular Expressions, que es la que he seguido aquí. El modelo POSIX está obsoleto en PHP a partir de su versión 5.3.

Entre otras cuestiones, esto ha dejado invalidadas algunas funciones que no vale la pena ver aquí (las ereg... y split). En su lugar, hay que usar estas:

Conclusión: las expresiones regulares son muy útiles y tienen la virtud de que no ocupan espacio en la memoria personal, pues a la media hora se te han olvidado por completo :P.

Adenda

Borja me da a conocer una página chulísima para construir expresiones regulares: http://gskinner.com/RegExr/

|| Tags: ,

valoración de los lectores sobre Introducción a las expresiones regulares en PHP

  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • 4.8 sobre 5 (24 votos)

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

10 respuestas a “Introducción a las expresiones regulares en PHP

  1. papulo (@papulo) el dijo:

    Excelente post,

    me ha aclarado el tema de manera clara y amena.

    Da gusto leer cosas así.

  2. Felix Barros el dijo:

    Sin duda uno de los mejores articulo de introducción a las expresiones regulares que he leído! 🙂

  3. alberto el dijo:

    saludos, como hago para conseguir un patron de este tipo: por ejemplo
    GP-12345-12. esos 2 caracter, seguido de un guion, 5 digitos o mas otro guin y termina con 2 digitos o mas

  4. Buenas noches
    Estoy leyendo su tutorial porque necesito remover con preg_replace los enlaces de un string.
    Lo consigo con la siguiente linea:
    preg_replace(‘/(.*?)/’, “\\2”, $limpiado1);

    El problema es que algunos de los textos de los enlaces tienen un salto de linea, y entonces estos enlaces no son removidos. Y no entiendo el por qué.

    Por ejemplo, quita las etiquetas <a href… escritas de esta forma:

    Texto sin salto

    Pero no quita los escritos así:

    Texto con salto

    Si pudieras echarme un cable, para quitar todos los enlaces, incluso los que en su texto tienen un salto de linea, t elo agradezco mucho, porque seguro que para alguien como tu es muy sencillo.