Expresiones regulares

Sábado, 17 de enero de 2026

El uso de las expresiones regulares o "regex" facilita la búsqueda, extracción y manipulación de texto; desde buscar patrones en fuentes grandes de datos, ya sean estructuradas o no, y extraer el dato buscado, realizar sustituciones precisas de caracteres o cadenas de texto, validar información, etc.

Los lenguajes de programación más utilizados incorporan regex de forma nativa (Perl, C++, .NET, PHP, Java, Python...) y también C mediante la biblioteca PCRE.

Las regex se empezaron a incorporar a partir de la década de 1960 en lenguajes de programación como SNOBOL o herramientas como QED o ed, pero fue con la llegada del lenguaje Perl en 1987 cuando se popularizaron en el uso del incipiente Internet por su facilidad en la manipulación de texto.

Los sistemas operativos UNIX, GNU/Linux, IOS incorporan comandos que utilizan regex donde destacan "grep" y "sed". En Windows, Powershell también incorpora un motor regex.

Fue con este tutorial de Perl donde las descubrí y a medida que leía me daba cuenta de su gran potencial.

Por ejemplo, si necesitamos saber si un texto concreto es una dirección de mail, sin utilizar regex tendríamos que iterar por cada caracter del texto y comparar cada uno de ellos con los que sean correctos (letras, números, algunos caracteres especiales, la arroba por medio, etc.), con la complejidad añadida de que el número de caracteres no es fijo. Otra opción es utilizar alguna librería externa que se dedique a este tipo de validaciones (seguramente utilizaría regex). En cualquier caso, implicaría varias e incluso muchas líneas de código o tener dependencias de otras librerías.


Validar información



En cambio, utilizando regex puedes validar una dirección de mail de cualquier longitud con una sola línea (aunque la regex es críptica cuando no la conoces). Así sería con Perl:

if ( $email =~ /^[a-zA-Z0-9.-_]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ ) {
  print "$email es una dirección de email válida\n";
}
            

Este if se cumple cuando $email es una dirección de mail. Vamos por partes:

$email =~ /regex/
$email contiene el texto para validarlo como una dirección de email, "=~" es el operador de Perl para indicar una regex a continuación entre barras. La barra normal de este tipo también se denomina "slash".

A continuación, la regex en sí:

^
Asegura que la coincidencia empieza al principio de la cadena de texto.

[a-zA-Z0-9.-_]+
Busca uno o más "[...]+" caracteres alfanuméricos, puntos ".", guiones bajos "_", guiones "-" o guiones bajos "_") para el nombre de usuario.

@
Busca el símbolo "@".

[a-zA-Z0-9.-]+
Busca uno o más "[...]+" caracteres alfanuméricos, puntos o guiones para el nombre del dominio.

\.
Al punto le precede un back slash (barra invertida) para "escaparlo" de forma que la regex lo interprete de forma literal. Sin back slash el punto significa "cualquier caracter" y no es el caso. Este punto separa el nombre del dominio principal del dominio de primer nivel o extensión de dominio. Es importante distinguir este uso para escapar el punto del uso donde no hace falta escaparlo y vimos anteriormente dentro de los corchetes [a-zA-Z0-9.-].

[a-zA-Z]{2,}
Busca dos o más ([...]{2,}) letras para la extensión del dominio (ej: com, org, net).

$
Asegura que la coincidencia termina al final de la cadena de texto.

Como vemos, la precisión en la búsqueda de caracteres determinados en un orden y número determinado es ideal para identificar cualquier patrón de texto.

Este funcionamiento también es ideal para validar campos de un formulario y así evitar problemas de seguridad, permitiendo solo caracteres de confianza, como letras, números y símbolos o caracteres especiales que carezcan de significado en código.


Parseando logs



Otra aplicación interesante de las regex es la extracción de información determinada en ficheros log, muy útil para facilitar diagnósticos y estadísticas.

Por ejemplo, el siguiente script de Perl extrae la IP de origen y el recurso solicitado de cada petición HTTP de tipo GET que recibe un servidor web Apache2:

# Abrir el archivo de log en $fh
open(my $fh, '<', '/var/log/apache2/access.log') or die "No se puede abrir el archivo: $!";

# Procesar el archivo línea por línea
while (my $linea = <$fh>) {

  if ( $linea =~ /(^\d+\.\d+\.\d+\.\d+).*?"GET\s(.*?)\s/ ) {
    print "$1 $2\n";
  }
}

# Cerrar el archivo de log
close($fh);
            

Como vemos, en el "if" buscamos al principio de la línea "^" la IP en forma de uno o más números "\d+" separados por un punto "\.". Todo ello entre paréntesis, que quiere decir que lo vamos a capturar para extraerlo.

Después indicamos ".*?" que quiere decir "cualquier texto con cualquier longitud lo más ajustado posible hasta que se encuentre lo próximo a buscar", esto es: un punto es cualquier caracter (menos el salto de línea), el asterísco significa "ninguno o cualquier número de coincidencias del caracter de la izquierda", que sería cualquier texto y "?" significa "ajustar lo más posible lo anterior hasta que se encuentre lo próximo a buscar" que será...

Que será el símbolo de doble comilla ("). Este símbolo, en el log de acceso de Apache2 que estamos utilizando, indica el comienzo del nombre del recurso de la petición GET y que en nuestro caso queremos capturar.

Después indicamos "GET" que es el método HTTP que queremos buscar en este caso (aunque también hay otros como POST, PUT, etc.).

A continuación indicamos un espacio con "\s".

Ahora viene la siguiente parte que nos interesa capturar (el recurso solicitado a Apache2) entre paréntesis "(.*?)" que es, como hemos visto antes, cualquier texto ajustado hasta el próximo espacio (de nuevo "\s") que es donde finaliza el texto que nos interesa.

Finalizada la expresión regular, si el "if" se cumple mostrará la IP que es el primer grupo de captura mediante la variable $1 y el recurso en el siguiente grupo de captura que es $2.

Por ejemplo, de la siguiene línea de log:

24.34.203.222 - - [14/Jan/2026:22:15:09 +0100] "GET /index.php HTTP/1.1"
            

Nuestro script de Perl mostraría la IP que es "24.34.203.222" y el recurso solicitado que es "/index.php" de la siguiente forma:

24.34.203.222 /index.php
            

Creando un listado con todas las peticiones recibidas de tipo GET.

Cuando comprendes cómo funciona una regex de este tipo, es más sencillo el análisis de un texto dado, averiguar cómo aislar la información deseada, extraerla y aplicar este conocimiento en cualquier log de cualquier aplicación.

Además de "parsear" logs, ("parsear" es una forma común de llamar al análisis de texto), el uso de regex para extraer información puede aplicarse a cualquier fuente de texto y estructurar la información obtenida guardándola en una base de datos para procesarla después.


Sustitución y eliminación de texto



Por último, vamos a ver dos de las aplicaciones más útiles y prácticas de las regex: la sustitución o eliminación masiva de texto en ficheros de texto. Para el siguiente ejemplo utilizaremos el comando "sed" disponible en sistemas operativos *NIX, GNU/Linux e IOS.

Supongamos un fichero HTML denominado "index.html" que incluye varias cabeceras entre las etiquetas "<h1>" y "</h1>", y por alguna razón necesitamos cambiarlas por "<h2>" y "</h2>"; pues tan sencillo como ejecutar dos veces el comando "sed" de la siguiente forma:

sed -i 's/<h1>/<h2>/g' index.html
sed -i 's/</h1>/</h2>/g' index.html
            

-i
indica que la operación se realizará en el fichero indicado

La operación se define entre comillas simples:

s
indica "sustitución"

Después se indica la barra "slash", el texto a buscar, otro "slash" y el texto que sustituirá al texto buscado, cerrando la operación de sustitución con otro "slash".

g
significa que sustituya todas las ocurrencias.

Y por último indicamos el nombre del fichero HTML donde necesitamos realizar las sustituciones.

Pero... ¿aquí no hay regex?; efectivamente que no hay, en este caso no estamos buscando un patrón o regex sino un texto literal que actúa como tal, por ejemplo "<h1>". Si bien en este caso no aplica porque no es necesario, sí podemos utilizar regex con "sed" para eliminar valores.

Imagina que en el mismo fichero "index.html" necesitas eliminar todos los enlaces, como por ejemplo éste:

<a href="https://direccion.com" target="_blank">Este es el enlace</a>
            

Todos los enlaces que puedan existir en nuestro fichero index.htlm tienen dos "marcas" que no cambian nunca, una de comienzo de la etiqueta de enlace <a href=" y otra para el final de la etiqueta de enlace que es </a>. Entre entre estas marcas se encuentra la dirección del enlace, algún parámetro y su texto, que pueden variar entre enlaces a lo largo del fichero index.html, tanto en sus caracteres como en su longitud.

Eliminar estos enlaces con el comando "sed" sería así:

sed -i -E 's/<a href=".*?<\/a>//g' index.html
            

Aquí solo hay dos cosas nuevas:

El parámetro "-E" dota a "sed" del motor de regex de Perl, para que sea más potente y tenga todas sus características. En este caso, sin este parámetro la regex no funciona.

Entre los "slash" existe la regex que queremos buscar pero después no existe el texto con el cual queremos realizar la sustitución "//". Efectivamente, de esta forma estamos realizando una sustitución por ningún texto, esto es, estamos eliminando el texto encontrado por la regex. Pruébalo.


Conclusión



Si bien al principio el uso de las regex es críptico, una vez comprendido, es el medio más sencillo y efectivo de buscar, extraer y manipular texto. Ideal para casos puntuales como para operaciones intensivas y masivas.