PHP-TUT-13 Expresiones regulares

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail
Expresiones regulares

Expresiones regulares

Las expresiones regulares constituyen una de las herramientas más flexibles y potentes a la hora de procesar cadenas alfanuméricas en cualquier lenguaje de programación moderno. Tanto es así que, a pesar de haber hablado en un artículo anterior del tratamiento de cadenas, hemos reservado a este tema un articulo propio, no tanto por la extensión del tema en sí, como por su trascendencia.

A lo largo de tu vida profesional desarrollarás proyectos que requieran la entrada de datos del usuario a través de campos de texto. En ellos el usuario escribe “libremente” lo que desea. Y es normal que, en ocasiones, cometa errores. Todos nos equivocamos de tecla alguna vez al mecanografiar.

El uso de expresiones regulares permite filtrar, hasta cierto punto, algunos errores que podrían causar unos resultados incorrectos o un mal funcionamiento de nuestra aplicación. A este efecto me vienen a la memoria las palabras del que fue mi primer profesor de informática: “Es posible diseñar aplicaciones a prueba de errores, pero es imposible diseñar una aplicación a prueba de usuarios”. Bromas aparte, supón que en tu sitio web el usuario debe teclear una dirección de correo electrónico donde, por ejemplo, se le mandarán las respuestas a sus consultas, la información que solicite en un momento dado, etc. Y supón que, por un descuido, el usuario no teclea el signo de la arroba que separa el nombre del dominio. Cuando la aplicación intente enviarle un correo electrónico (lo que será tema para un artículo posterior), se producirá un error.

El uso de expresiones regulares nos permitirá detectar que hay un error en la dirección de e-mail, y remitir al usuario a una página que le informe de dicho error y le dé opción a introducir correctamente su dirección.

A lo largo de este capítulo veremos qué son, exactamente, las expresiones regulares, cómo funcionan y cómo nos pueden ayudar a mejorar la eficiencia de nuestra programación.

QUÉ SON LAS EXPRESIONES REGULARES

Las expresiones regulares son, básicamente, patrones con los que comparar una cadena, obteniendo un resultado lógico de tipo true si la cadena se ajusta al patrón y un false en caso contrario. Así pues, podremos crear un patrón que contenga la arquitectura típica de una dirección de correo electrónico y después comparar las cadenas que introduzcan los usuarios con dicho patrón. Cuando el resultado de esa comparación sea true, la ejecución continuará en la siguiente página del sitio. Si el resultado es false, la ejecución saltará a una página en la que se le pida al usuario que vuelva a introducir el dato.

Los patrones se construyen a base de lo que se conoce como metacaracteres, que son unos caracteres que, dentro de este entorno, tienen un significado específico. Además, se pueden incluir en las expresiones regulares caracteres que sólo se representan a sí mismos.

INTRODUCCIÓN A LOS PATRONES

En esta sección vamos a ver cómo se crearán los patrones que luego se usarán para filtrar cadenas. Los conceptos que vamos a ver ahora son generales de las expresiones regulares y se ajustan al estándar empleado por PHP 7. Más adelante entraremos en detalles de cada uno de ellos.

Los patrones que se usan para construir expresiones regulares se pueden considerar clasificados en cuatro categorías:

  • De secuencia o fijación.
  • Multiplicadores.
  • De alternativa.
  • Paréntesis.

PATRONES DE SECUENCIA O FIJACIÓN

El patrón más simple está compuesto por un solo carácter. Por ejemplo a. Con esta expresión coincidirán todas las cadenas que contengan la letra a (minúscula). Así pues, coincidirán "Sonia", "Eva" y "Jaime" pero no coincidirán "Pedro", "coche" ni "globo", por ejemplo. Éste sería un ejemplo de una expresión regular formada por un carácter que se representa a sí mismo.

La expresión más simple, usando un metacarácter es . (el punto), que representa a un carácter cualquiera. Por supuesto, un patrón así sería demasiado ambiguo: con él coincidirá cualquier cadena que tenga, al menos, un carácter cualquiera, es decir, que no sea una cadena vacía.

A partir de este concepto ya podemos pensar en expresiones regulares más complejas. Por ejemplo a.t. Con esta expresión coincidirán todas las cadenas que contengan la letra a, a continuación un carácter cualquiera y la letra t. Por ejemplo, "masticar", "aptitud" y "antiguo". Como ves, estas palabras cumplen la condición especificada.

Los metacaracteres [ y ] permiten referirse a un rango de caracteres que definamos para buscar coincidencias. Por ejemplo, supongamos el patrón [abcde]. Con él coincidirá cualquier cadena que contenga alguna de las letras especificadas, como "Coco", "sábado" o "Septiembre". No coincidirán "tifus", "sino" ni "divino".

Cuando los caracteres que forman parte de un rango tienen valores ASCII consecutivos se puede expresar el rango mediante el primer carácter (el de código ASCII más bajo), seguido de un guión y el último carácter (el de código ASCII más alto). El ejemplo del patrón anterior podría quedar así: [a-e]. Si tienes alguna duda acerca del código ASCII, puede mirarlo en este enlace.

En ocasiones queremos usar las expresiones regulares para identificar cadenas que no contengan caracteres de un determinado rango. Para ello usamos el metacarácter ^, anteponiéndolo al primer carácter del rango. Suponte que quieres identificar cadenas que NO contengan las letras de la a a la e (minúsculas). El patrón adecuado será [^a-e].

A menudo debemos crear una expresión destinada a identificar un carácter al principio o al final de una cadena. Si creamos un patrón como ^F, coincidirán con él todas las cadenas que empiecen con la F (mayúscula). No confundas este uso del metacarácter ^ con el de negar un rango. La diferencia de sintaxis es que, para negar un rango, el metacarácter y el rango van encerrados ente [ y ] (corchetes).

Para determinar que una cadena debe acabar con un carácter concreto pondremos el metacarácter ^ a continuación de aquel que queremos establecer como final. Si fijamos el patrón a^, coincidirán con él todas las cadenas que terminen con la a minúscula.

PATRONES MULTIPLICADORES

Son aquellos que se usan para repetir un carácter en la expresión regular. Supón que quieres una expresión tal que coincidan con ella todas las cadenas que tengan tres letras a (minúscula) seguidas. Podrías crear un patrón como aaa, pero es más adecuado a{3}. Al expresar entre llaves un número a continuación de un carácter indicamos que, para coincidir con la expresión regular, la cadena debe incluir ese carácter repetido tantas veces como indica el número (por supuesto, seguidas). Con el patrón especificado en el ejemplo coincidirán las cadenas "lkjfsaaalkdsf", "094r0aaaaa0f04w3i" y "lsrfkjpoigkjeaaa". Fíjate en que da igual si hay más de los tres caracteres (como es el caso de la segunda cadena): sólo se busca la existencia de, al menos, tres.

Para comprobar si un carácter se repite entre n y m veces estableceremos los dos límites, entre llaves, a continuación de dicho carácter. Suponte el patrón a{2,5}. Con él concordarán todas las cadenas en las que se encuentre el carácter a entre dos y cinco veces seguidas. Si aparece menos de dos veces (una o ninguna) o más de cinco, la cadena no coincidirá con la expresión.

Existe otro multiplicador, en el que se pone el límite inferior de veces que queremos que aparezca el carácter en la cadena, a fin de determinar si concuerda o no. Este límite se sigue de una coma, sin límite superior, así: a{2,}. Coincidirán todas las cadenas que tengan, al menos, dos veces seguidas la a (minúscula; en este ejemplo no se contarían las mayúsculas).

El multiplicador * a continuación de un carácter determina que, para que la cadena coincida con la expresión, dicho carácter debe repetirse cualquier número de veces (incluso ninguna). Si te pones a pensarlo, verás que este multiplicador carece de uso práctico.

El multiplicador +, a continuación de un carácter determina que, para que haya coincidencia, dicho carácter debe estar presente, al menos, una vez en la cadena.

El multiplicador ? a continuación de un carácter indica que, para que la cadena coincida, dicho carácter deberá estar presente una vez, o ninguna. No habrá coincidencia si aparece más de una vez.

PATRONES DE ALTERNATIVA

Supongamos una expresión como la siguiente: a|b|c. El carácter que separa la primera letra de la segunda, y ésta de la tercera, es la barra que se emplea en los operadores OR lógicos (usados en condicionales), aunque en este caso sólo una vez (|) y no dos (||). Recuerda más a los operadores a nivel de bit, de los que ya hemos hablado en un artículo anterior. Con este patrón coinciden todas las cadenas que incluyan la letra a, o bien la b, o la c. Como es lógico, la cadena también coincidirá si incluye dos de las tres, o las tres.

Si te pones a pensarlo, verás que, en el ejemplo concreto que he puesto aquí, también sería equivalente un rango como el siguiente: [abc], o bien [a-c]. Sin embargo, mediante el metacarácter de alternativa, al contrario de lo que sucede con los rangos, los caracteres no tienen por qué ser consecutivos según el código ASCII. Así pues, podemos crear una expresión como x|j, con la que coincidirán las cadenas que incluyan la x o la j (o ambas).

LOS PARÉNTESIS

Los paréntesis tienen una triple finalidad. Por una parte, se emplean para agrupar patrones simples, como los que hemos visto en los ejemplos anteriores,  formando patrones más complejos y, por lo tanto, más selectivos a la hora de que una cadena coincida con ellos. Por otra parte, nos permitirán memorizar (ya veremos cómo hacerlo, más adelante) partes de una cadena que coincidan con la expresión. Además, tienen un uso adicional que veremos en este mismo artículo.

El uso de paréntesis permite, como hemos dicho, agrupar patrones simples. Por ejemplo, suponte que quieres crear un multiplicador que busque en la cadena a comparar la secuencia j2 tres veces seguidas. Podría crear el patrón j2j2j2, pero mejor solución es la siguiente: (j2){3}.

ESCAPADO DE METACARACTERES. Seguramente a estas alturas te habrás preguntado cómo hacer que una expresión regular busque en la cadena, por ejemplo, un punto. Es decir, si creas el patrón ., se dará la coincidencia con cualquier carácter, ya que el punto no es un carácter que se represente a sí mismo, sino un metacarácter. Para ello recurrimos al escapado de metacaracteres, mediante un contraslash.

En el patrón \., el punto se ha convertido en un carácter que se representa a sí mismo, con lo que, para que haya coincidencia en la cadena deberá aparecer un punto. Si quieres escapar más de un metacarácter debes hacerlo con cada uno, por ejemplo: \.\{\[.

PRECEDENCIA DE PATRONES

Como ya he comentado recientemente, los patrones se pueden agrupar para formar expresiones regulares complejas que permitan hacer una comprobación selectiva de cadenas. Supongamos un patrón como el siguiente: j|x^. Hay que tener cuidado al establecer un patrón así. Según lo leemos, puede significar que buscamos una cadena que acabe en x o en j. O puede significar que buscamos una cadena que contenga una j o que acabe en x. Esto es lo que se llama una ambigüedad programática. A veces implican una resolución implícita por la propia naturaleza del lenguaje, y otras no. Esas últimas son las más peligrosas, porque, en el mejor de los casos, darán un error en ejecución. En el peor, no darán error, pero los resultados serán imprevisibles. Volviendo a nuestra expresón regular, ¿cuál es la interpretación correcta? Tal como está planteado el patrón, la segunda. Existe un orden establecido entre los metacaracteres, conocido como precedencia (es el caso de un mecanismo de resolución implícita de una ambigüedad). El metacarácter de fijación ^ tiene mayor precedencia que el de alternativa |. Por esta razón se ejecuta antes. Y, ¿cómo podemos hacer que la expresión tenga la primera interpretación? Aquí es donde entran en juego de nuevo los paréntesis. Nos permiten alterar la precedencia de los metacaracteres. En este ejemplo escribiremos un patrón como el siguiente: (j|x)^. Esto hará que coincidan con él todas las cadenas que acaben en j o en x. La precedencia natural de los metacaracteres es la siguiente:

  • Paréntesis.
  • Multiplicadores.
  • Secuencia o fijación.
  • Alternativa.

Para ver cómo podemos combinar varios patrones para formar una expresión regular, vamos a crear una que permita identificar si una cadena es correcta como dirección de correo electrónico. La solución es la siguiente:

^([a-z]|[0-9])+(\.?([a-z]|[0-9])+)?@([a-z]|[0-9]){2,}(\.[a-z]{2,3})(\.[a-z]{2})?^

Como ves, la expresión resultante es bastante más compleja de lo que uno puede imaginar al principio, pero si la analizamos veremos que es un uso de los patrones que ya conocemos. En primer lugar, fíjate en que hay un signo @, obligatorio en toda dirección de correo electrónico. Debe aparecer uno, y sólo uno, separando el nombre del usuario del nombre del servidor.

Empecemos por analizar la parte de la izquierda, destinada al nombre del usuario. Vemos que la cadena que concuerde con esta expresión debe comenzar con, al menos, una letra o dígito, tal como indica el patrón ^([a-z]|[0-9])+. A continuación, puede haber un carácter punto \. y una o más letras o dígitos. Esta última parte puede darse una vez o ninguna, tal como indica el signo ? que aparece al final. Esto es porque los nombres de usuario de las direcciones de correo electrónico pueden, en determinados servidores, estar formadas por dos partes, separadas entre sí por un punto. En este caso, no hemos tenido en cuenta el hecho de que el nombre de usuario de una dirección válida puede incluir el guión bajo, para simplificar un poco el ejemplo, ya que es el primero que vemos tan, digamos, complejo.

Después de la comprobación del signo @, encontramos la parte de la expresión destinada a comprobar el nombre del servidor. El nombre puede ser tan sencillo como servidor.es o servidor.com o puede estar formado por tres partes, tal como servidor.com.ar o servidor.co.uk. En cualquier caso, debe haber una primera parte del nombre del servidor que contenga, al menos, dos letras o dígitos. Es el patrón ([a-z]|[0-9]){2,} el encargado de comprobar que así sea. A continuación, puede haber un punto y dos o tres caracteres. El patrón (\.[a-z]{2,3}) comprueba que esta primera terminación sea correcta. Opcionalmente puede haber otra terminación formada por un punto y dos letras, tal como nos dice el patrón (\.[a-z]{2}). Esta última terminación puede darse una vez, o ninguna, según especifica el signo ?. Por último, está el ^, encargado de establecer que la cadena debe terminar ahí. No puede haber nada más a continuación.

Una observación final. La cadena con una dirección de correo sólo concuerda con la expresión regular si todas sus letras son minúsculas. En este caso eso no es un problema, ya que así es como se escriben las direcciones de e-mail. Más adelante veremos cómo evitar esta limitación si es necesario.

COMPROBACIÓN DE CADENAS

Ya es hora de que podamos comprobar personalmente qué cadenas concuerdan con qué expresiones regulares y cuáles no lo hacen. A tal fin, usaremos el listado compruebaExpresion.htm, reproducido a continuación:

No tiene nada especial. Es sólo un formulario en el que introduces una expresión regular y una cadena que quieres cotejar con la expresión. Cuando envíes el formulario, el script compruebaExpresion.php que lo recibe comprobará si la cadena concuerda con la expresión o no.

Ten en cuenta que aún no hemos explicado la mecánica de las funciones destinadas a comprobar si una cadena concuerda con una expresión regular o no. Por ahora sólo veamos cómo opera. Al cargar compruebaExpresion.htm en el navegador verás un formulario muy simple.

En el campo Expresión regular introduce la expresión que hemos visto anteriormente para la comprobación de direcciones de correo electrónico. En el campo cadena introduce tu propia dirección de e-mail y pulsa COMPROBAR. El formulario será enviado al script correspondiente, que le mostrará una página en la que se te informa de que la expresión y la cadena concuerdan.

Vamos a “bucear” en el listado. La comparación es muy fácil, empleando la función preg_match(), que devuelve un valor booleano, indicando si la cadena coincide con la expresión.

ATENCIÓN. Las funciones que empiezan por ereg_ y que, tradicionalmente se venían empleando para comparar cadenas con expresiones regulares fueron declaradas obsoletas en PHP 5.3 y en PHP 7 no existen y no funcionan. Si tienes listados antiguos que las incluyan, ya es hora de empezar a adaptarlos.

Veamos otro código.

Si tenemos que incluir una expresión regular fija en nuestro código, lo haremos con comillas dobles, como si de una cadena se tratara.

 

     

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *