Expresiones regulares y lenguajes de programación II: PHP y UNIX

Bienvenidos a una nueva y, probablemente última, entrada sobre expresiones regulares. En este caso continuamos el ciclo de las Expresiones regulares y lenguajes de programación para hablar sobre el uso de las expresiones regulares en PHP y en comandos de UNIX.

Si no sabes qué son las expresiones regulares, puedes leer nuestra entrada sobre teoría básica de expresiones regulares.
Si por el contrario quieres probar tus propias expresiones regulares o símplemente trastear un poco, también contamos con un testeador web de expresiones regulares.
Y si por otra razón estás buscando cómo utilizar expresiones regulares en Java o en Javascript, puedes visitar nuestra entrada sobre el uso de expresiones regulares en Java y Javascript.

Para esta entrada, por tanto, vamos a seguir la dinámica de la anterior del mismo ciclo: uso de expresiones regulares en un lenguaje usado en la web y en un lenguaje no-web.

Como dijimos en la entrada anterior de la serie, vamos a utilizar la siguiente expresión regular para los ejemplos (más información aquí):

^(\w+)@(\w+)\.([a-zA-z]{2,6})$
 

PHP


PHP son las siglas de PHP Hypertext Processor, muy al estilo de las siglas GNU. PHP es un lenguaje de programación web interpretado, para ejecutar código en el lado del servidor (al contrario que Javascript, por ejemplo). Por esta razón, el código PHP es invisible al navegador web de forma que hace fiable y seguro (relativamente, existen fallas) el uso de estos códigos. Desde su versión 5 además, acepta orientación a objetos.

Como en todos los ejemplos, tenemos dos opciones para tratar las expresiones regulares en PHP. Una consiste en la verificación del patrón con una cadena de texto y la otra consiste en el reemplazo de una cadena de texto utilizando una expresión regular.

Veamos primero las funciones que se usan para validar las cadenas de texto contra una expresión regular:
  • Función preg_match(): esta función recibe la expresión regular que se va a usar en sintaxis de Perl, la cadena de texto contra la que vamos a validar y, opcionalmente, un array en el que se guardarán las partes encajadas por los paréntesis. Antes existía la función ereg() que, básicamente, hacía lo mismo que esta función pero recibía la expresión regular como un string. Sin embargo, desde la versión 5.3.0 de PHP, la función ereg() ha pasado al status de Deprecated, por lo que se recomienda usar esta en su lugar. Pongamos un par de ejemplos para el uso de esta función. Usando sólo la validación (0 == false, cualquier otra cosa == true)
    <?php
     // Meta | vidasconcurrentes.blogspot.com
     echo preg_match('/^(\w+)@(\w+)\.([a-zA-z]{2,6})$/',
          'test@vidasconcurrentes.com');
     // output: 1
    ?>
    
    Usando tanto la validación como el array (vemos los grupos que encajan):
    <?php
     // Meta | vidasconcurrentes.blogspot.com
     echo preg_match('/^(\w+)@(\w+)\.([a-zA-z]{2,6})$/',
          'test@vidasconcurrentes.com', $capturas);
     echo '<br/ >';
     $i = 0;
     print_r($capturas);
     // output: 1
     //         Array
     //         (
     //             [0] => test@vidasconcurrentes.com
     //             [1] => test
     //             [2] => vidasconcurrentes
     //             [3] => com
     //         )
    ?>
    
  • Función preg_replace(): esta función recibe la expresión regular (o un array de expresiones regulares) que va a usar en sintaxis de Perl, la cadena de reemplazo (o un array de reemplazos), y la cadena sobre la que se va a reemplazar. La forma más simple para ver cómo funciona son un par de casos de ejemplo:
    <?php
     // Meta | vidasconcurrentes.blogspot.com
     echo preg_replace('/(\w+)/', 'prueba',
          'test@vidasconcurrentes.com');
     // output: prueba@prueba.prueba
    ?>
    También podemos querer hacer un reemplazo selectivo. Por ejemplo, todos los números cambiarlos por algo, todos los símbolos de suma cambiarlos por el símbolo de resta, todas las cadenas que contengan ae cambiarlas por e... Para esto vamos a usar la misma función pero usando arrays. En nuestro caso vamos a cambiar todas las palabras por la cadena letras, todos los números por la cadena numeros y todos los guiones (símbolo de resta) por guiones bajos:
    <?php
     // Meta | vidasconcurrentes.blogspot.com
     $regexes = array('/[a-zA-Z]+/', '/\d+/', '/\-/');
     $replacements = array('letras', 'numeros', '_');
     echo preg_replace($regexes, $replacements, 
          'perro test 123 prueba - - -');
     // output: letras letras numeros letras _ _ _
    ?>
Para más información sobre las expresiones regulares en PHP y otras funciones no mencionadas aquí, recomendamos visitar la referencia del grupo de funciones de expresiones regulares en PHP.

El código completo de este ejemplo puede encontrarse en nuestro repositorio.


Expresiones regulares en UNIX


En los sistemas operativos UNIX, contamos con el comando sed que puede utilizar expresiones regulares y hacer reemplazos cadenas de texto. Pongamos un par de ejemplos que pueden ser de utilidad:

  • Digamos que somos administradores de un sistema, y que tenemos una aplicación web en la que los usuarios se registran con sus datos, entre ellos un teléfono. Lo normal es que los clientes escriban el teléfono correspondiente de la forma que ellos quieran. De modo que podemos encontrarnos con diferentes formatos. Entonces nuestro objetivo es conseguir que todos los teléfonos tengan el mismo formato, independientemente de cómo lo haya escrito cada usuario. Supongamos que nuestro formato objetivo fuera: XX-XXX-XX-XX. También digamos que contamos con un fichero de texto como el siguiente (los teléfonos no son reales, solamente son una serie de números tecleados aleatoriamente):
    21-346-23-14
    21 346 23 14
    21-3462314
    21-346 23 14
    (21)346 23 14
    213 462 314
    213-462-314
    Aquí podemos comprobar que cada usuario habría escrito su teléfono de una forma diferente. Lo que nosotros queremos es que todos los teléfonos (que en este caso son iguales pero no necesariamente debería ser así), tuvieran la pinta del primero: 21-346-23-14. Digamos que el fichero anterior se llamara telefonos.txt. En nuestro terminal preferido en nuestro sistema UNIX podríamos ejecutar:
    # Meta | vidasconcurrentes.blogspot.com
    cat telefonos.txt | sed 's/[-() ]//g' | sed 's/\([0-9]\{2\}\)
                      \([0-9]\{3\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)/\1\-\2\-\3\-\4/'
    Esta linea leerá el fichero de texto y lo volcará a su salida estándar, que en este caso será la entrada del primer comando sed. Éste, va a borrar todos los guiones, paréntesis y espacios, para dar a todos los teléfonos un pre-formato (en este caso, las 9 cifras seguidas sin caracteres intermedios), y va a volcar su salida por su salida estándar... que a su vez es la entrada del nuevo comando sed. Éste último va a coger cada teléfono y va a intentar identificar cuatro grupos: uno de dos cifras que, por ejemplo para Madrid, será el prefijo; otro de tres cifras, y dos grupos más de dos cifras. Y entre cada grupo va a incluir un guión. La salida de este comando será la consola aunque podríamos escribirlo diréctamente en un fichero de texto o lo que se nos ocurriera. De cualquier manera, la salida sería:
    21-346-23-14
    21-346-23-14
    21-346-23-14
    21-346-23-14
    21-346-23-14
    21-346-23-14
    21-346-23-14
  • En este caso vamos a seguir con el ejemplo de que somos administradores de una base de datos de usuarios. Supongamos en este caso que los usuarios necesitan escribir su fecha de nacimiento para, más tarde, hacer comparaciones de edad. Este es un ejemplo que no es lo más eficiente a la hora de hacer esta tarea, pero para lo que estamos explicando nos vale. La idea es que cada usuario habrá escrito su fecha de nacimiento de diferentes formas (en formato español en este caso), y nosotros necesitamos que todas tengan un formato específico (formato internacional) para luego poder compararlo. Digamos que tenemos un fichero de texto con fechas de nacimiento:
    28-12-1969
    09/09/1941
    03 12 1950
    Nuestro objetivo es que todas las fechas tengan el formato internacional, que es: AAAA-MM-DD. Para conseguir esto, podríamos escribir en la consola (y suponiendo que nuestro fichero se llamase fechas.txt):
    # Meta | vidasconcurrentes.blogspot.com
    cat fechas.txt | sed 's/\([0-9]\{1,2\}\)[- \/]
                     \([0-9]\{1,2\}\)[- \/]\([0-9]\{4\}\)/\3\-\2\-\1/'
    En este caso lo que estamos haciendo es coger cada línea (que hemos supuesto que solo contendrá fechas), partir por trozos de la forma: una o dos cifras, un guión/barra/espacio, una o dos cifras, un guión/barra/espacio, cuatro cifras... y luego rehacer esa línea de forma que lo primero será el año (las 4 cifras), un guión, el mes, un guión, y el día. El resultado de ejecutar este comando saldría por consola y sería:
    1969-12-28
    1941-09-09
    1950-12-03
    
Existe también posibilidad de utilizar expresiones regulares con el comando grep, para UNIX.


Aquí acaba esta entrada, y con ella el ciclo de expresiones regulares. Hemos aprendido qué son las expresiones regulares, cómo usar expresiones regulares en Javascript, Java, PHP y en el comando sed de UNIX. Lógicamente esto no significa que sepamos usar expresiones regulares como auténticos gurús, y los ejemplos vistos son sólo una ínfima parte de lo que se puede hacer con las expresiones regulares.

Como further reading recomendamos comenzar por la página de referencia de expresiones regulares, que incluye una sección de uso en lenguajes de programación.

Un saludo para los lectores.