Este manual sólo pretende ser una pequeña guía introductoria a las expresiones regulares, empezando por lo básico para hacer expresiones sencillas o adaptar otras a nuestras necesidades.
Por supuesto, puede contener errores o cosas que se podrían mejorar. Para cualquier sugerencia, cuestión o duda podeis contactar conmigo en .
Espero que a alguien le resulte útil.
- ¿Qué son las expresiones regulares?
- Algunos tipos de expresiones regulares
- Cómo podemos usarlas
- Empecemos: las unidades "átomo"
- El carácter comodín
- "Escapar" caracteres
- Otros caracteres especiales
- Repetición de átomos avanzada
- Grupos de caracteres. Rangos.
- Reemplazos avanzados
- Combinaciones
- Notas al utilizar grep
- Conclusiones
Colores:
En el manual las expresiones regulares se representarán con letra Courier y color marrón:
expresión regular
Lo que esté en Courier pero sin estar en marrón pueden ser cadenas o algo "literal".
1. ¿Qué son las expresiones regulares?
Antes de empezar a hablar de ellas mejor dejar claro al principio qué son :-D.
A veces necesitamos encontrar algo concreto en un texto o cadena, o reemplazar algo por otra cosa; ya sea en una aplicación, o en un lenguaje de programación. Por ejemplo si queremos buscar "tag" y reemplazarlo por "etiqueta" la mayoría de aplicaciones o lenguajes tienen una función para hacerlo de forma sencilla.
Pero a veces lo que queremos hacer es más complejo, porque puede que en vez de ser una palabra o parte de palabra simple, necesitemos hacer algo como "búscame todas las palabras que acaben en 'f' y que empiecen por un número del 2 al 71" (por ejemplo) o "reemplaza las palabras que contengan este grupo de letras por esto".
En estos casos podemos utilizar las expresiones regulares (que se pueden llamar regex o regexp de forma abreviada), que es como un lenguaje para poder definir exactamente qué es lo que queremos buscar o reemplazar.
2. Algunos tipos de expresiones regulares
Hemos dicho antes que las expresiones regulares son como un lenguaje para definir lo que queremos buscar de forma exacta, pero este lenguaje no siempre es el mismo, quiero decir, hay varios "tipos" de lenguajes.
Los que más se suelen utilizar son dos:
- Expresiones regulares POSIX extendido
- Expresiones regulares de Perl
Aunque son muy parecidas no son exactamente iguales. En este manual veremos generalmente el primer tipo (Expresiones regulares POSIX) ya que son más fáciles para empezar.
Las expresiones del segundo tipo hay que "encerrarlas" entre unos caracteres especiales, normalmente se utiliza la barra inclinada /. Con las expresiones POSIX (las que veremos aquí) no hace falta.
Por ejemplo, para saber si un texto contiene "foo" se haría así en las dos expresiones:
POSIX ==> foo
Perl ==> /foo/
Sí, "foo" también es una expresión regular aunque evidentemente es el tipo más sencillo que podemos hacer 🙂
A partir de ahora en esta guía hablaremos principalmente de las expresiones regulares POSIX, aunque la mayoría también se harán de igual manera en Perl pero encerrandolas entre caracteres (entre barras por ejemplo).
Cuando una expresión regular "encaja" con una cadena, línea o lo que sea se dice que coincide, o match en inglés.
Para poder utilizar las expresiones regulares necesitamos "algo" que nos permita usarlas (sea una aplicación o lenguaje de programación).
En entornos UNIX hay muchas utilidades que nos permiten usarlas. Es el caso de 'GNU Grep' o 'sed'.
Para utilizar las expresiones POSIX extendido con "grep" hay que pasarle el parámetro "E" seguido de la expresión regular.
Por ejemplo:
$ ls | grep -E <expresión>
Lo que haría sería devolvernos los nombres de archivos del directorio actual que coincidieran con la expresión <expresión> (sin los > y < ).
En lenguajes de programación: como ejemplo veremos cómo utilizarlas en PHP, ya que es un lenguaje que nos permite usar los dos tipos de expresiones regulares y además es un lenguaje bastante utilizado y no hace falta compilar.
Si no te interesa cómo utilizarlas en PHP puedes y empezar con las expresiones regulares :-).
La función más básica para usar una expresión regular POSIX en PHP es ereg.
$resultado = ereg("<expresión>",$cadena);
Nos devolverá TRUE si <expresión> ha coincidido en $cadena o FALSE si no lo ha hecho.
También podemos utilizar eregi, que hace lo mismo que ereg pero no distingue entre mayúsculas y minúsculas.
Para reemplazar en PHP tenemos ereg_replace (o su variante eregi_replace ).
$cadena = ereg_replace("<expresión>","<reemplazo>",$cadena);
Ese ejemplo aplicará la sustitución de <expresión> por <reemplazo> a $cadena. Cuando más adelante veamos el apartado de sustituciones veremos que en <reemplazo> también podemos incluír grupos que coincidieron en <reemplazo>.
También hay más funciones que permiten utilizar expresiones regulares pero como esto no es un manual de PHP sólo veremos lo básico.
4. Empezamos: las unidades "átomo"
En expresiones regulares la "unidad" se llama "átomo" y generalmente cada letra es un átomo. Por ejemplo en la expresión regular
baz
cada letra es una unidad "átomo".
Sin embargo, a veces queremos que un átomo sea más grande. Por ejemplo, más adelante veremos modificadores que permiten coincidir la expresión si el átomo se repite N veces. Si queremos que lo que se pueda repetir sea un grupo de letras, podemos hacer que éstas formen un átomo poniéndolas entre paréntesis:
foo(bar)baz
Aquí las letras de "baz" seguirían siendo átomos sueltos pero el grupo "bar" sería un átomo todo entero.
Aparte los paréntesis tienen más utilidades que veremos más adelante.
5. El carácter comodín
Un caso muy típico en expresiones regulares es que a veces no sabemos exactamente qué letra queremos buscar por ejemplo en medio de una palabra, pero queremos utilizar un "comodín", o sea, cualquier letra.
En expresiones regulares POSIX el caracter "comodín" es el punto (.).
Veamos un ejemplo: la expresión regular
l.ca
coincidiría tanto con
laca
como con
loca
Como podemos ver el. representa cualquier carácter.
Como hemos visto en los capítulos 4 y 5 en las expresiones regulares existen caracteres especiales que hacen una función (agrupar, comodines, etc.).
Pero ¿y si queremos representar el propio carácter en vez de su función?
Quiero decir, en el capítulo anterior veíamos esta expresión:
l.ca
Donde el "." coincidía con cualquier carácter (incluido el propio punto, claro).
Pero ¿y si queremos que el "." no tenga esa función, o sea, que la expresión sólo coindica si encuentra de verdad l.ca no laca ni loca ni leca ?.
A esto se le llama "escapar" y se hace con la barra invertida ( ). Así, para este ejemplo quedaría así:
l.ca
Con lo cual ya sólo coincidiría con "l.ca".
También podemos escapar la doble barra:
/
🙂
7. Otros caracteres especiales
El carácter circunflejo (^). Coincide con el principio de una línea.
Por ejemplo, si queremos hacer coincidir las líneas que empiecen por http de una cadena, podríamos usar la expresión:
^http
Con lo cual sólo si el http está a principio de línea coincidiría la expresión, si http está en el medio o final no.
En ese sentido, coincidiría con "http://www.google.com" pero no con "El protocolo http es el que..:".
El dólar ($): Coincide con el final de una línea (al contrario que la anterior).
Por ejemplo, si queremos hacer coincidir las líneas que acaben por com de una cadena, podríamos usar la expresión:
com$
Con lo cual sólo si el com está a final de línea coincidiría la expresión, si com está en el medio o final no.
En ese sentido, coincidiría con "www.google.com" pero no con "www.google.com.ar".
Puede o no puede aparecer: El carácter "interrogante" (?): Si colocamos un interrogante después de un átomo, estamos indicando que el átomo puede o no puede aparecer, la expresión coincidirá en los dos casos. Ejemplo:
foo(baz)?bar
Que coincidiría con "foobazbar", y con "foobar". Pero no con "foobazbazbar".
Repetición de átomos: El carácter asterisco (*): Si colocamos un asterisco después de un átomo estamos indicando que ese átomo se repite cero o más veces, o sea, que puede que no aparezca el átomo, que aparezca una vez o que lo haga las veces que sean; con el asterísco coincidirá siempre. Ejemplo:
go*gle
Que coincidiría con "gogle", "google", "goooooogle", "gooooooooooooooooogle", pero también con "ggle".
Otro ejemplo:
foo(bar)*baz
Que coincidiría con "foobarbaz", "foobarbarbaz", "foobarbarbarbarbarbaz", "foobaz", pero no con "foobarrrrrrrbaz" ni con "foobar".
Repetición de átomos: El carácter "más" (+): Se comporta de forma parecida al anterior, pero éste, al colocarlo después de un átomo estamos indicando que el átomo se repite al menos alguna vez. Ejemplo:
go+gle
Que coincidiría con "gogle", "google", "goooooogle", "gooooooooooooooooogle", pero no con "ggle".
Otro ejemplo:
foo(bar)+baz
Que coincidiría con "foobarbaz", "foobarbarbaz", "foobarbarbarbarbarbaz", pero no con "foobaz", con "foobarrrrrrrbaz" ni con "foobar".
El carácter (|): Equivale a "ó". Por ejemplo foobar|foobaz coincidiría tanto con "foobar" como con "foobaz".
8. Repetición de átomos avanzada
En el capítulo anterior hemos visto cómo podemos indicar que un átomo podía repetirse. Pero no teníamos mucho control sobre la repetición ya que utilizando esos caracteres, ésta siempre era infinita.
Utilizando los corchetes después del átomo podemos indicar qué tipo de repetición con más exactitud.
"Se repite N veces". Si dentro de los corchetes ponemos un número, estamos indicando que el átomo al que hacen referencia los corchetes se repite ese número de veces.
Ejemplo:
go{4}gle
Coincidirá con "goooogle" pero no con "gogle", con "google" ni con "ggle".
"Se repite de N a M veces". Si dentro de los corchetes ponemos dos números separados por comas, estamos indicando que el átomo al que hacen referencia los corchetes se repite las veces que sea del primer número al segundo.
Ejemplo:
go{4,8}gle
Coincidirá con "goooogle", "gooooogle", "goooooogle", "goooooooogle" pero no con "gooogle", con "google" ni con "ggle".
También si el segundo número no es nada ( por ejemplo {4,} ) quiere decir del primer número al infinito.
9. Grupos de caracteres. Rangos.
A veces en una expresión regular queremos que coincida con una letra cualquiera dentro de un grupo de letras.
Los grupos de caracteres se colocan entre "corchetes planos" [ y ], y no hace falta separarlos.
Por ejemplo:
s[ae]ca
Coincidiría tanto como con "seca" como con "saca" pero no con "saeca" ni con "seaca".
Dentro de un grupo de caracteres pueden haber los que hagan falta.
Dentro de un grupo de caracteres si ponemos el carácter ^ al principio, el significado del grupo es al revés: coincide con cualquier carácter que no esté en el grupo.
Por ejemplo:
s[^iou]ca
Coincidiría tanto como con "seca" como con "saca" pero no con "soca" ni con "suca" ni con "sica".
Rangos
Dentro de un grupo de caracteres también podemos especificar rangos: de tal carácter a tal carácter, o sea, cualquier carácter que esté comprendido entre A y B. Tenemos en cuenta que si es una letra el orden es el alfabético (abcdefg…) y si es un número, orden numérico (123456…).
Ejemplo:
[0-9] y [0-9]
Que coincidirá con "2 y 8", "9 y 7", "2 y 0"… pero no con "24 y 36" (si queremos que lo haga tendríamos que aplicar algo de lo que hemos aprendido: ([0-9])+ y ([0-9])+ ).
También pueden estar rangos y caracteres sueltos en un mismo grupo.
Ejemplo:
f[^w-z0-9a]o
Que coincidiría con "foo", "feo", "fso" pero no con "fwo", "fyo", "fzo", "f2o" ni "fao".
Dentro de una expresión regular, a la hora de reemplazar podemos hacer referencia a algo que depende de lo que contenga un grupo que hemos definido antes, en la expresión.
Los grupos de paréntesis avanzados nos permiten definir zonas ( entre ( y ) ). El contenido (variable según lo que definamos dentro) podemos referenciarlo en lo que vamos a reemplazar, utilizando la barra invertida y un número del 0 al 9 según la posición del grupo dentro de la expresión. Ejemplo:
Buscar (([0-9])+) al cubo y reemplazar por 1 por si mismo tres veces
En ese mismo ejemplo, el 1 haría referencia al primer paréntesis.
Al aplicarlo a "48 al cubo" nos lo convertiría en "48 por si mismo tres veces".
Notad que hemos puesto dos paréntesis porque el primero sólo haría referencia al último número (ya que [0-9] es sólo un número del 0 al 9…)
En PHP el ejemplo anterior podría ser así:
<?php
$cadena = "474 al cubo";
$reemplazada = ereg_replace('(([0-9])+) al cubo','1 por si mismo tres veces',$cadena);
print $reemplazada;
?>
Como obtenemos más potencia en una expresión regular es combinando técnicas.
Por ejemplo, hemos visto cómo repetir átomos. Si lo que queremos es hacer coincidir una zona donde pueda ir "cualquier cosa" podemos utilizar el átomo ".". Por ejemplo:
{(.+)}
Coincidiría con cualquier cosa que estuviera entre corchetes. Por ejemplo con "{Foo}". Además a la hora de reemplazar podríamos utilizar 1 para referirnos a lo que hay dentro del paréntesis como hemos visto antes…
Con 'grep', como vimos antes podemos utilizar expresiones regulares perfectamente.
Sólo hay que tener en cuenta una cosa: algunos caracteres como los paréntesis o la misma barra invertida, el intérprete "bash" intentará interpretarlos antes de pasárselo a 'grep'. Por lo tanto debemos escaparlo con (el dólar sería $, los paréntesis (, la barra invertida /…)
Por ejemplo, para que nos devuelva las líneas de "archivo.txt" que contengan números:
$ cat archivo.txt | grep -E ([0-9])+
Recuerda que con "grep", dominando las expresiones regulares, puedes hacer de todo, es una utilidad potentísima 🙂
Y sobre todo utilizando las "pipes" (|) de UNIX que redirigen la salida de otro comando al siguiente… 🙂
Por ejemplo, para enviar un correo a prueba[arroba]yahoo.com conteniendo los archivos de /ejemplo que sean.tar.gz o.tgz :
$ ls /ejemplo | grep -E /.tar/.gz$|/.tgz$ | mail prueba[arroba]yahoo.com
😀
Espero que te haya sido útil el manual :-). Y eso no es todo, en este manual hemos visto las expresiones regulares POSIX extendido pero una vez que domines éstas también puedes aprender las de Perl, que son parecidas pero aparte son mucho más potentes, ya que tienen modificadores que hacen de todo 🙂
Eso sí, como son algo más liosas te recomiendo dominar antes bien las POSIX 🙂
Este manual se distribuye bajo licencia Free Distribution License (FDL), por supuesto puedes copiar, redifundir o redistribuirlo.
(C)opyLeft 2004, Toad