Saltar a contenido
 No os perd谩is mi futuro contenido, seguidme en y Youtube 馃榿

Expresiones regulares

Una de las tareas m谩s utilizadas en la programaci贸n es la b煤squeda de subcadenas o patrones dentro de otras cadenas de texto.

Las expresiones regulares, tambi茅n conocidas como 'regex' o 'regexp', son patrones de b煤squeda definidos con una sintaxis formal. Siempre que sigamos sus reglas, podremos realizar b煤squedas simples y avanzadas, que utilizadas en conjunto con otras funcionalidades, las vuelven una de las opciones m谩s 煤tiles e importantes de cualquier lenguaje.

Sin embargo antes de utilizarlas hay que estar seguros de lo que hacemos, de ah铆 aquella famosa frase de Jamie Zawinski, programador y hacker:

Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.

Que viene a decir:

Hay gente que, cuando se enfrenta a un problema, piensa "Ya s茅, usar茅 expresiones regulares". Ahora tienen dos problemas.

M茅todos b谩sicos

  • re.search: busca un patr贸n en otra cadena:

Note

import re

texto = "En esta cadena se encuentra una palabra m谩gica"

re.search('m谩gica', texto)
<_sre.SRE_Match object; span=(40, 46), match='m谩gica'>

Como vemos, al realizar la b煤squeda lo que nos encontramos es un objeto de tipo Match (encontrado), en lugar un simple True o False.

En cambio, si no se encontrase la palabra, no se devolver铆a nada (None):

Note

import re
re.search('hola', texto)

Por tanto, podemos utilizar la propia funcionalidad junto a un condicional sin ning煤n problema:

Note

palabra = "m谩gica"

encontrado = re.search(palabra,  texto)

if encontrado:
    print("Se ha encontrado la palabra:", palabra)
else:
    print("No se ha encontrado la palabra:", palabra)
Se ha encontrado la palabra: m谩gica

Sin embargo, volviendo al objeto devuelto de tipo Match, 茅ste nos ofrece algunas opciones interesantes.

Note

# Posici贸n donde empieza la coincidencia
print( encontrado.start() ) 
# Posici贸n donde termina la coincidencia
print( encontrado.end() )  
# Tupla con posiciones donde empieza y termina la coincidencia
print( encontrado.span() )   
# Cadena sobre la que se ha realizado la b煤squeda
print( encontrado.string )   
40
46
(40, 46)
En esta cadena se encuentra una palabra m谩gica

Como vemos, en este objeto se esconde mucha m谩s informaci贸n de la que parece a simple vista, luego seguiremos hablando de ellos.

  • re.match: busca un patr贸n al principio de otra cadena:

Note

texto = "Hola mundo"

re.match('Hola', texto)
<_sre.SRE_Match object; span=(0, 4), match='Hola'>
  • re.split: divide una cadena a partir de un patr贸n:

Note

texto = "Vamos a dividir esta cadena"

re.split(' ', texto)
['Vamos', 'a', 'dividir', 'esta', 'cadena']
  • re.sub: sustituye todas las coincidencias en una cadena:

Note

texto = "Hola amigo"

re.sub('amigo', 'amiga', texto)
'Hola amiga'
  • re.findall: busca todas las coincidencias en una cadena:

Note

texto = "hola adios hola hola"

re.findall('hola', texto)
['hola', 'hola', 'hola']

Aqu铆 se nos devuelve una lista, pero podr铆amos aplicar la funci贸n len() para saber el n煤mero:

Note

len(re.findall('hola', texto))
3

Patrones con varios valores

Si queremos comprobar varias posibilidades, podemos utilizar una tuber铆a | a modo de OR. Generalmente pondremos el listado de alternativas entre par茅ntesis ():

Note

texto = "hola adios hello bye"

re.findall('hola|hello', texto)
['hola', 'hello']

Patrones con sintaxis repetida

Otra posibilidad que se nos ofrece es la de buscar patrones con letras repetidas, y aqu铆 es donde se empieza a poner interesante. Como podemos o no saber de antemano el n煤mero de repeticiones hay varias formas de definirlos.

Note

texto = "hla hola hoola hooola hooooola"

Antes de continuar, y para aligerar todo el proceso, vamos a crear una funci贸n capaz de ejecutar varios patrones en una lista sobre un texto:

Note

def buscar(patrones, texto):
    for patron in patrones:
        print( re.findall(patron, texto) )

patrones = ['hla', 'hola', 'hoola']
buscar(patrones, texto)
['hla']
['hola']
['hoola']
  • Con meta-car谩cter *

Lo utilizaremos para definir ninguna o m谩s repeticiones de la letra a la izquierda del meta-car谩cter:

Note

patrones = ['ho','ho*','ho*la','hu*la']

buscar(patrones, texto)
['ho', 'ho', 'ho', 'ho']
['h', 'ho', 'hoo', 'hooo', 'hooooo']
['hla', 'hola', 'hoola', 'hooola', 'hooooola']
['hla']
  • Con meta-car谩cter +

Lo utilizaremos para definir una o m谩s repeticiones de la letra a la izquierda del meta-car谩cter:

Note

patrones = ['ho*', 'ho+']  

buscar(patrones, texto)
['h', 'ho', 'hoo', 'hooo', 'hooooo']
['ho', 'hoo', 'hooo', 'hooooo']
  • Con meta-car谩cter ?

Lo utilizaremos para definir una o ninguna repetici贸n de la letra a la izquierda del meta-car谩cter:

Note

patrones = ['ho*', 'ho+', 'ho?', 'ho?la']

buscar(patrones, texto)
['h', 'ho', 'hoo', 'hooo', 'hooooo']
['ho', 'hoo', 'hooo', 'hooooo']
['h', 'ho', 'ho', 'ho', 'ho']
['hla', 'hola']
  • Con n煤mero de repeticiones expl铆cito {n}

Lo utilizaremos para definir 'n' repeticiones exactas de la letra a la izquierda del meta-car谩cter:

Note

patrones = ['ho{0}la', 'ho{1}la', 'ho{2}la']

buscar(patrones, texto)
['hla']
['hola']
['hoola']
  • Con n煤mero de repeticiones en un rango {n, m}

Lo utilizaremos para definir un n煤mero de repeticiones variable entre 'n' y 'm' de la letra a la izquierda del meta-car谩cter:

Note

patrones = ['ho{0,1}la', 'ho{1,2}la', 'ho{2,9}la']

buscar(patrones, texto)
['hla', 'hola']
['hola', 'hoola']
['hoola', 'hooola', 'hooooola']

Conjuntos de caracteres

Cuando nos interese crear un patr贸n con distintos car谩cteres, podemos definir conjuntos entre par茅ntesis:

Note

texto = "hala hela hila hola hula"

patrones = ['h[ou]la', 'h[aio]la', 'h[aeiou]la']
buscar(patrones, texto)
['hola', 'hula']
['hala', 'hila', 'hola']
['hala', 'hela', 'hila', 'hola', 'hula']

Evidentemente los podemos utilizar con repeticiones:

Note

texto = "haala heeela hiiiila hoooooola"

patrones = ['h[ae]la', 'h[ae]*la', 'h[io]{3,9}la']
buscar(patrones, texto)
[]
['haala', 'heeela']
['hiiiila', 'hoooooola']
  • Exclusi贸n en grupos [^ ]:

Cuando utilizamos grupos podemos utilizar el operador de exclusi贸n ^ para indicar una b煤squeda contraria:

Note

texto = "hala hela hila hola hula"

patrones = ['h[o]la', 'h[^o]la'] 
buscar(patrones, texto)
['hola']
['hala', 'hela', 'hila', 'hula']
  • Rangos [ - ]:

Otra caracter铆stica que hace ultra potentes los grupos, es la capacidad de definir rangos. Ejemplos de rangos:

  • [A-Z]: Cualquier car谩cter alfab茅tico en may煤scula (no especial ni n煤mero).
  • [a-z]: Cualquier car谩cter alfab茅tico en min煤scula (no especial ni n煤mero).
  • [A-Za-z]: Cualquier car谩cter alfab茅tico en min煤scula o may煤scula (no especial ni n煤mero).
  • [A-z]: Cualquier car谩cter alfab茅tico en min煤scula o may煤scula (no especial ni n煤mero).
  • [0-9]: Cualquier car谩cter num茅rico (no especial ni alfab茅tico).
  • [a-zA-Z0-9]: Cualquier car谩cter alfanum茅rico (no especial).

Tened en cuenta que cualquier rango puede ser excluido para conseguir el patr贸n contrario.

Note

texto = "hola h0la Hola mola m0la M0la"

patrones = ['h[a-z]la', 'h[0-9]la', '[A-z]{4}', '[A-Z][A-z0-9]{3}'] 
buscar(patrones, texto)
['hola']
['h0la']
['hola', 'Hola', 'mola']
['Hola', 'M0la']

C贸digos escapados \

Si cada vez que quisi茅ramos definir un patr贸n variable tuvi茅ramos que crear rangos, al final tendr铆amos expresiones regulares gigantes. Por suerte su sintaxis tambi茅n acepta una serie de caracteres escapados que tienen un significo 煤nico. Algunos de los m谩s importantes son:

C贸digo Significado
\d num茅rico
\D no num茅rico
\s espacio en blanco
\S no espacio en blanco
\w alfanum茅rico
\W no alfanum茅rico

El problema que encontraremos en Python a la hora de definir c贸digo escapado, es que las cadenas no tienen en cuenta el \ a no ser que especifiquemos que son cadenas en crudo (raw), por lo que tendremos que precedir las expresiones regulares con una 'r'.

Note

texto = "Este curso de Python se public贸 en el a帽o 2016"

patrones = [r'\d+', r'\D+', r'\s', r'\S+', r'\w+', r'\W+'] 
buscar(patrones, texto)
['2016']
['Este curso de Python se public贸 en el a帽o ']
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
['Este', 'curso', 'de', 'Python', 'se', 'public贸', 'en', 'el', 'a帽o', '2016']
['Este', 'curso', 'de', 'Python', 'se', 'public贸', 'en', 'el', 'a帽o', '2016']
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']

Por mi parte lo vamos a dejar aqu铆, pero el mundo de las expresiones regulares es gigantesco y dar铆a para un curso entero. Os animo a seguir aprendiendo leyendo documentaci贸n y buscando ejemplos, os dejo algunos enlaces que os podr铆an servir.

Documentaci贸n

Hay docenas y docenas de c贸digos especiales, si quer茅is echar un vistazo a todos ellos pod茅is consultar la documentaci贸n oficial:

Un resumen por parte de Google Eduacti贸n:

Otro resumen muy interesante sobre el tema:

Un par de documentos muy trabajados con ejemplos b谩sicos y avanzados:


脷ltima edici贸n: 6 de Octubre de 2018