Introducción a las Expresiones Regulares en Python
Las expresiones regulares, a menudo abreviadas como regex o regexp, son una potente herramienta para la búsqueda y manipulación de texto basada en patrones. Son secuencias de caracteres que definen un patrón de búsqueda. En Python, el módulo re proporciona una implementación completa de las expresiones regulares al estilo Perl, ofreciendo funcionalidades para comparar, buscar, reemplazar y dividir cadenas de texto. Desde la versión 1.5, este módulo ha sido una parte fundamental de la biblioteca estándar de Python.
El módulo re permite trabajar con expresiones regulares de dos maneras principales: utilizando funciones directas que toman el patrón como primer argumento, o compilando el patrón en un objeto RegexObject con la función re.compile(). Compilar patrones es útil para reutilizarlos eficientemente o para aplicar banderas específicas de forma persistente. A continuación, exploraremos las funciones y métodos más utilizados del módulo re en Python.
La función re.match()
La función re.match(pattern, string, flags=0) en Python busca un patrón de expresión regular exclusivamente al comienzo de una cadena. Si el patrón coincide desde el primer carácter, devuelve un objeto de tipo MatchObject; de lo contrario, retorna None. Es crucial entender que re.match() no escaneará la cadena completa; su búsqueda se limita al inicio.
Parámetros de re.match():
pattern: La expresión regular a buscar.string: La cadena de texto donde se realizará la búsqueda.flags: (Opcional) Modificadores que alteran el comportamiento de la búsqueda (ej., insensibilidad a mayúsculas/minúsculas, modo multilínea).
Si se encuentra una coincidencia, el objeto MatchObject resultante posee métodos útiles para acceder a los resultados:
group(num=0): Retorna la subcadena que coincide con el patrón completo (num=0), o con un grupo específico si se proporciona su índice. Puede recibir múltiples índices y devolver una tupla.groups(): Devuelve una tupla que contiene todas las subcadenas que coincidieron con los grupos de la expresión regular, excluyendo el grupo 0.
Ejemplos de re.match():
import re
# Coincidencia desde el inicio
result_start = re.match('inicial', 'inicial.ejemplo.com')
if result_start:
print(f"Coincidencia al inicio: {result_start.span()}")
else:
print("No hubo coincidencia al inicio.")
# No hay coincidencia si el patrón no está al principio
result_middle = re.match('ejemplo', 'inicial.ejemplo.com')
if result_middle:
print(f"Coincidencia en el medio: {result_middle.span()}")
else:
print("No hubo coincidencia para 'ejemplo' al inicio.")
import re
texto_animales = "Los gatos son más inteligentes que los perros"
# Captura dos grupos: el sujeto y el adjetivo comparativo
patron_animal = r'(.*) son (.*?) que los perros'
coincidencia_animal = re.match(patron_animal, texto_animales, re.I | re.M)
if coincidencia_animal:
print("Texto completo coincidente:", coincidencia_animal.group())
print("Primer grupo (sujeto):", coincidencia_animal.group(1))
print("Segundo grupo (adjetivo):", coincidencia_animal.group(2))
else:
print("No se encontró ninguna coincidencia en la frase.")
La función re.search()
A diferencia de re.match(), la función re.search(pattern, string, flags=0) explora la totalidad de la cadena en busca de la primera ubicación donde el patrón de expresión regular coincida. Tan pronto como encuentra una coincidencia, devuelve un objeto MatchObject. Si el patrón no se encuentra en ninguna parte de la cadena, re.search() retorna None.
Parámetros de re.search():
Los parámetros son idénticos a los de re.match():
pattern: La expresión regular a buscar.string: La cadena de texto donde se realiazrá la búsqueda.flags: (Opcional) Modificadores de comportamiento.
El objeto MatchObject devuelto por re.search() también ofrece los métodos group() y groups() con la misma funcionalidad.
Ejemplos de re.search():
import re
cadena_web = 'www.miempresa.org'
# Búsqueda desde el inicio
res_inicio = re.search('www', cadena_web)
if res_inicio: print(f"Patrón 'www' encontrado en: {res_inicio.span()}")
# Búsqueda en cualquier posición
res_dominio = re.search('empresa', cadena_web)
if res_dominio: print(f"Patrón 'empresa' encontrado en: {res_dominio.span()}")
import re
frase_original = "Los coches son más rápidos que las motos"
# Buscar un patrón que capture el sujeto y el adjetivo
patron_vehiculo = r'(.*) son (.*?) que las motos'
coincidencia_vehiculo = re.search(patron_vehiculo, frase_original, re.M | re.I)
if coincidencia_vehiculo:
print("Coincidencia completa:", coincidencia_vehiculo.group())
print("Sujeto capturado:", coincidencia_vehiculo.group(1))
print("Adjetivo capturado:", coincidencia_vehiculo.group(2))
else:
print("No se halló el patrón deseado.")
Diferencias clave entre re.match() y re.search()
La principal distinción entre estas dos funciones radica en el ámbito de su búsqueda:
re.match(): Intenta encontrar el patrón solo al principio de la cadena de texto. Si la coincidencia no se produce exactamente en el índice 0, la función devuelveNone.re.search(): Escanea la cadena completa desde el inicio hasta el final, devolviendo la primera coincidencia que encuentre en cualquier posición.
Este comportamiento se ilustra mejor con un ejemplo:
import re
texto_comparacion = "manzana es mejor que naranja"
# Intento con re.match()
match_resultado = re.match(r'naranja', texto_comparacion, re.I)
if match_resultado:
print(f"re.match encontró: {match_resultado.group()}")
else:
print("re.match: 'naranja' no se encontró al inicio de la cadena.")
# Intento con re.search()
search_resultado = re.search(r'naranja', texto_comparacion, re.I)
if search_resultado:
print(f"re.search encontró: {search_resultado.group()}")
else:
print("re.search: No se encontró 'naranja' en la cadena.")
Sustitución de patrones con re.sub()
El método re.sub(pattern, repl, string, count=0, flags=0) es fundamental para modificar cadenas de texto, permitiendo reemplazar todas o algunas ocurrencias de un patrón de expresión regular con una subcadena o el resultado de una función. Este método devuelve la cadena modificada.
Parámetros de re.sub():
pattern: La expresión regular a buscar en la cadena.repl: Puede ser una cadena de reemplazo o una función que se llamará por cada coincidencia encontrada. Si es una cadena, se pueden usar referencias a grupos (\1,\g<name>).string: La cadena de texto original donde se realizará la sustitución.count: (Opcional) El número máximo de reemplazos a realizar. Si es 0 (por defecto), se sustituyen todas las ocurrencias.flags: (Opcional) Modificadores que afectan la coincidencia del patrón.
Ejemplos de re.sub():
import re
texto_contacto = "Mi teléfono es 123-456-7890 # Número de casa"
# 1. Eliminar comentarios (todo lo que va después de '#')
sin_comentario = re.sub(r'#.*$', '', texto_contacto)
print(f"Número sin comentario: {sin_comentario.strip()}")
# 2. Eliminar caracteres no numéricos
solo_numeros = re.sub(r'\D', '', texto_contacto)
print(f"Solo números: {solo_numeros}")
repl como función
Cuando repl es una función, esta se invoca para cada coincidencia del patrón. La función recibe un objeto MatchObject como argumento, y su valor de retorno será la cadena utilizada para el reemplazo.
import re
# Función para duplicar el valor numérico de cada coincidencia
def duplicar_numero(coincidencia):
valor = int(coincidencia.group('valor')) # Acceder al grupo llamado 'valor'
return str(valor * 2)
cadena_con_numeros = 'El producto A cuesta 25 EUR, el B 100 USD.'
# El patrón usa un grupo con nombre (?P<name>...)
cadena_modificada = re.sub(r'(?P<valor>\d+)', duplicar_numero, cadena_con_numeros)
print(f"Cadena con números duplicados: {cadena_modificada}")
</valor>
Compilación de expresiones regulares con re.compile()
La función re.compile(pattern, flags=0) es utilizada para compilar un patrón de expresión regular en un objeto RegexObject. Este objeto compilado puede ser luego utilizado con métodos como match(), search(), findall(), sub(), etc. La compilación es particularmente útil cuando se planea usar el mismo patrón varias veces, ya que optimiza el rendimiento.
Sintaxis y Parámetros:
pattern: La expresión regular como cadena de texto.flags: (Opcional) Una combinación de modificadores que afectan cómo se interpreta el patrón (ver tabla de modificadores más adelante).
El objeto RegexObject resultante tiene métodos idénticos a las funciones a nivel de módulo (match, search, etc.), pero operan directamente sobre el objeto.
Ejemplos con patrón compilado:
import re
# Compilamos un patrón para encontrar uno o más dígitos
patron_digitos = re.compile(r'\d+')
texto_mixto = 'letra123palabra456final'
# Usando el objeto compilado con match()
# No hay coincidencia al inicio
coincidencia1 = patron_digitos.match(texto_mixto)
print(f"Match al inicio (fallido): {coincidencia1}")
# Match buscando desde el índice 5 (donde '1' está)
coincidencia2 = patron_digitos.match(texto_mixto, 5)
print(f"Match desde índice 5 (exitoso): {coincidencia2}")
if coincidencia2:
print("Grupo 0 (todo el match):", coincidencia2.group(0))
print("Posición de inicio:", coincidencia2.start(0))
print("Posición de fin:", coincidencia2.end(0))
print("Rango (inicio, fin):", coincidencia2.span(0))
Un objeto MatchObject (como coincidencia2 en el ejemplo anterior) proporciona métodos para obtener información detallada sobre la coincidencia:
group([idx1, ...]): Recupera la subcadena que coincide con el patrón completo (idx=0) o con un grupo específico.start([idx]): Devuelve el índice de inicio de la coincidencia del patrón completo o de un grupo específico.end([idx]): Devuelve el índice final (exclusivo) de la coincidencia del patrón completo o de un grupo específico.span([idx]): Retorna una tupla(start(), end())para el patrón completo o un grupo.
Ejemplo con grupos y banderas:
import re
# Compilamos un patrón para dos palabras, ignorando mayúsculas/minúsculas
patron_palabras = re.compile(r'(\w+) (\w+)', re.I)
texto_frase = 'Hola Mundo Asombroso'
match_frase = patron_palabras.match(texto_frase)
if match_frase:
print("Objeto Match:", match_frase)
print("Coincidencia completa (grupo 0):", match_frase.group(0))
print("Rango de grupo 0:", match_frase.span(0))
print("Primer grupo:", match_frase.group(1))
print("Rango de primer grupo:", match_frase.span(1))
print("Segundo grupo:", match_frase.group(2))
print("Rango de segundo grupo:", match_frase.span(2))
print("Todos los grupos:", match_frase.groups())
# Intento acceder a un grupo inexistente
try:
match_frase.group(3)
except IndexError as e:
print(f"Error al acceder a grupo 3: {e}")
Encontrando todas las coincidencias con re.findall()
La función re.findall(pattern, string, flags=0) es ideal para extraer todas las subcadenas no superpuestas que coinciden con un patrón dado. Retorna una lista de todas las coincidencias encontradas. Si no se encuentran coincidencias, devuelve una lista vacía.
Sintaxis y Parámetros:
pattern: La expresión regular a buscar.string: La cadena de texto donde se busca.flags: (Opcional) Modificadores de comportamiento.
Ejemplos de re.findall():
import re
# Buscar todos los números en una cadena
cadena_datos = 'item_A_123, item_B_456, item_C_789'
numeros_encontrados = re.findall(r'\d+', cadena_datos)
print("Números identificados:", numeros_encontrados)
# Buscar números en un rango específico de la cadena
cadena_limitada = 'data_10_20_30_values'
numeros_limitados = re.findall(r'\d+', cadena_limitada, 0, 10) # Busca en 'data_10_20'
print("Números en rango limitado:", numeros_limitados)
Iterando sobre coincidencias con re.finditer()
Similar a re.findall(), la función re.finditer(pattern, string, flags=0) localiza todas las ocurrencias no superpuestas de un patrón. Sin embargo, en lugar de devolver una lista de cadenas, retorna un iterador que produce objetos MatchObject por cada coincidencia encontrada. Esto es eficiente para procesar grandes volúmenes de texto o cuando se necesita información detallada de cada coincidencia (como su posición).
Parámetros de re.finditer():
pattern: La expresión regular a buscar.string: La cadena de texto donde se realizará la búsqueda.flags: (Opcional) Modificadores.
Ejemplo de re.finditer():
import re
texto_con_ids = "ID:abc123val, ID:def456key, ID:ghi789data"
# Buscar todos los IDs alfanuméricos
iterador_coincidencias = re.finditer(r'[a-z]{3}\d{3}', texto_con_ids)
print("Coincidencias encontradas (usando iterador):")
for match_obj in iterador_coincidencias:
print(f" - Valor: {match_obj.group()}, Rango: {match_obj.span()}")
Dividiendo cadenas con re.split()
La función re.split(pattern, string, maxsplit=0, flags=0) divide una cadena de texto en una lista de subcadenas, utilizando las coincidencias de la expresión regular como delimitadores. Es similar al método split() de las cadenas, pero con la flexibilidad de los patrones regex.
Parámetros de re.split():
pattern: La expresión regular que define los delimitadores.string: La cadena a dividir.maxsplit: (Opcional) El número máximo de divisiones a realizar. Si es 0 (por defecto), se realizan todas las divisiones posibles.flags: (Opcional) Modificadores.
Si el patrón contiene grupos de captura, el texto coincidente de esos grupos también se incluirá en la lista resultante.
Ejemplos de re.split():
import re
# Dividir por caracteres no alfanuméricos
texto_csv = 'valor1, valor2; valor3. valor4'
partes1 = re.split(r'[,;.\s]+', texto_csv)
print("División simple:", partes1)
# Dividir pero manteniendo los delimitadores (usando grupos de captura)
partes2 = re.split(r'([,;.\s]+)', texto_csv)
print("División con delimitadores:", partes2)
# División con un número máximo de divisiones
partes3 = re.split(r' ', 'uno dos tres cuatro', maxsplit=1)
print("División limitada:", partes3)
# Un patrón que no encuentra coincidencias no divide la cadena
partes4 = re.split(r'z', 'hello world')
print("División sin coincidencia:", partes4)
Banderas y Modificadores de Expresiones Regulares
El comportamiento de una expresión regular se puede modificar mediante el uso de banderas (o flags). Estas se pasan como un argumento opcional a las funciones del módulo re o a re.compile(). Pueden combinarse utilizando el operador OR bit a bit (|).
| Modificador | Descripción |
|---|---|
re.I (o re.IGNORECASE) |
Realiza coincidencias sin distinguir entre mayúsculas y minúsculas. |
re.M (o re.MULTILINE) |
Cambia el comportamiento de ^ y $ para que coincidan con el inicio y fin de cada línea, no solo con el inicio y fin de la cadena completa. |
re.S (o re.DOTALL) |
Permite que el metacaracter . (punto) coincida con cualquier carácter, incluyendo los saltos de línea (\n). Sin esta bandera, . no coincide con \n. |
re.U (o re.UNICODE) |
(Obsoleto/Comportamiento por defecto en Python 3) Afecta la definición de \w, \W, \b, \B, \d, \D, \s, \S para que se base en las propiedades de caracteres Unicode. |
re.A (o re.ASCII) |
Hace que \w, \W, \b, \B, \d, \D, \s, \S coincidan solo con caracteres ASCII. |
re.X (o re.VERBOSE) |
Permite escribir expresiones regulares más legibles, ignorando espacios en blanco no escapados y comentarios (#) dentro del patrón. |
Sintaxis y Elementos de los Patrones de Expresiones Regulares
Los patrones de expresiones regulares son cadenas que contienen una combinación de caracteres literales y metacaracteres con significados especiales. Estos patrones describen secuencias de texto que se desean buscar. Es una buena práctica utilizar cadenas "raw" (precedidas por r) para los patrones de regex en Python (ej., r'patron'), ya que esto evita que Python interprete las barras invertidas (\) como secuencias de escape de cadena.
La siguienet tabla resume los metacaracteres y secuencias de escape más comunes en las expresiones regulares:
| Patrón | Descripción |
|---|---|
^ |
Coincide con el inicio de la cadena (o el inicio de una línea en modo re.M). |
$ |
Coincide con el final de la cadena (o el final de una línea en modo re.M). |
. |
Coincide con cualquier carácter, excepto el salto de línea (\n). Con la bandera re.S, coincide también con \n. |
[abc] |
Coincide con cualquiera de los caracteres dentro de los corchetes (ej., 'a', 'b' o 'c'). |
[^abc] |
Coincide con cualquier carácter excepto los listados dentro de los corchetes. |
R* |
Coincide con cero o más ocurrencias del patrón R precedente (comportamiento "codicioso"). |
R+ |
Coincide con una o más ocurrencias del patrón R precedente (comportamiento "codicioso"). |
R? |
Coincide con cero o una ocurrencia del patrón R precedente (comportamiento "codicioso"). |
R{n} |
Coincide exactamente con n ocurrencias del patrón R precedente. |
R{n,} |
Coincide con n o más ocurrencias del patrón R precedente. |
R{n,m} |
Coincide con n a m ocurrencias del patrón R precedente. |
A|B |
Coincide con A o con B. |
(R) |
Agrupa el patrón R y lo convierte en un grupo de captura. |
(?:R) |
Agrupa el patrón R pero sin capturarlo (grupo no capturador). |
(?P<name>R) |
Agrupa el patrón R y le asigna el nombre name (grupo de captura con nombre). |
(?=R) |
Aserción de mirada hacia adelante positiva (positive lookahead). Coincide si R está adelante, pero no consume caracteres. |
(?!R) |
Aserción de mirada hacia adelante negativa (negative lookahead). Coincide si R no está adelante. |
\d |
Coincide con cualquier dígito decimal (equivalente a [0-9]). |
\D |
Coincide con cualquier carácter que no sea un dígito (equivalente a [^0-9]). |
\s |
Coincide con cualquier carácter de espacio en blanco (espacio, tabulación, salto de línea, etc.). |
\S |
Coincide con cualquier carácter que no sea espacio en blenco. |
\w |
Coincide con cualquier carácter de palabra (alfanumérico y guion bajo). Equivalente a [a-zA-Z0-9_]. |
\W |
Coincide con cualquier carácter que no sea de palabra. |
\b |
Coincide con un límite de palabra. |
\B |
Coincide con un no-límite de palabra. |
\A |
Coincide solo con el inicio de la cadena. |
\Z |
Coincide solo con el final de la cadena. |
\n, \t, etc. |
Coincide con caracteres de escape estándar (salto de línea, tabulación, etc.). |
\1, \2, ... |
Referencia a un grupo de captura anterior por su número. |
Ejemplos Prácticos de Patrones de Expresiones Regulares
Coincidencia de Caracteres Literales
| Patrón | Descripción | Ejemplo de coincidencia |
|---|---|---|
python |
Coincide con la secuencia exacta de caracteres "python". | "python" en "programacion python" |
Python |
Coincide con "Python" (sensible a mayúsculas). | "Python" en "Aprende Python" |
Clases de Caracteres ([])
| Patrón | Descripción | Ejemplo de coincidencia |
|---|---|---|
[pP]ython |
Coincide con "Python" o "python". | "python" en "El mejor python" |
colou[ru] |
Coincide con "colour" (inglés británico) o "color" (inglés americano). | "color" en "mi color favorito" |
[aeiou] |
Coincide con cualquier vocal en minúscula. | 'a', 'e', 'i', 'o', 'u' |
[0-9] |
Coincide con cualquier dígito del 0 al 9. | '7' en "precio 7 euros" |
[a-z] |
Coincide con cualquier letra minúscula del alfabeto inglés. | 'x' en "texto" |
[A-Z] |
Coincide con cualquier letra mayúscula del alfabeto inglés. | 'G' en "GRUPO" |
[a-zA-Z0-9] |
Coincide con cualquier letra (mayúscula o minúscula) o dígito. | 'A', 'z', '5' |
[^aeiou] |
Coincide con cualquier carácter que NO sea una vocal minúscula. | 'p', 'y', 't', 'h', 'n' |
[^0-9] |
Coincide con cualquier carácter que NO sea un dígito. | 'a', '!', '%' |
Clases de Caracteres Especiales (\)
| Patrón | Descripción | Ejemplo de coincidencia |
|---|---|---|
. |
Coincide con cualquier carácter individual (excepto \n por defecto). |
'a' en "abc", '3' en "123" |
\d |
Coincide con un dígito (0-9). Equivalente a [0-9]. |
'8' en "número 8" |
\D |
Coincide con un no-dígito. Equivalente a [^0-9]. |
'A' en "ID-A" |
\s |
Coincide con un carácter de espacio en blanco (espacio, tabulación, salto de línea, etc.). | ' ' en "hola mundo" |
\S |
Coincide con un no-espacio en blanco. | 'c' en "cadena" |
\w |
Coincide con un carácter de palabra (letras, números, guion bajo). Equivalente a [a-zA-Z0-9_]. |
'word', 'count_1' |
\W |
Coincide con un no-carácter de palabra. Equivalente a [^a-zA-Z0-9_]. |
'!', '@', '#' |