W3docs

Python RegEx

Aprende expresiones regulares en Python: sintaxis, secuencias especiales, grupos, lookaheads, flags y funciones del módulo re con ejemplos claros.

Las expresiones regulares (regex) te permiten buscar, extraer y reemplazar texto basándote en patrones flexibles en lugar de cadenas exactas. El módulo integrado re de Python proporciona todo lo que necesitas. Este capítulo cubre el conjunto completo de herramientas de regex: sintaxis, secuencias especiales, cuantificadores, grupos, lookahead/lookbehind, flags y todas las funciones clave del módulo re, con ejemplos correctos y ejecutables a lo largo del texto.

¿Por qué usar expresiones regulares?

Los métodos de string simples (str.find(), str.replace(), str.split()) funcionan bien para texto fijo. Las expresiones regulares destacan cuando el patrón varía:

  • Validar que un string tenga la forma de una dirección de correo electrónico o número de teléfono.
  • Extraer todas las fechas de un documento, independientemente del formato exacto.
  • Eliminar etiquetas HTML de un string.
  • Reemplazar múltiples secuencias de espacios en blanco distintas por un único espacio.

Cuando la tarea implica describir una forma en lugar de un valor fijo, utiliza re.

Raw Strings

Los patrones de regex casi siempre se escriben como raw strings (r'...'). Los raw strings tratan las barras invertidas como caracteres literales, lo cual importa porque regex utiliza muchas secuencias con barra invertida (\d, \w, \s, \b). Sin el prefijo r necesitarías doble barra invertida en todas partes:

import re

# Both patterns are identical — raw string is easier to read
re.findall(r'\d+', 'abc 123')   # raw:    r'\d+'
re.findall('\\d+', 'abc 123')   # normal: '\\d+'

Usa raw strings para cada patrón de regex — es la convención universal.

Sintaxis básica: metacaracteres

Los metacaracteres son caracteres con significado especial dentro de un patrón. Los caracteres literales se corresponden exactamente consigo mismos.

MetacarácterSignificado
.Cualquier carácter excepto un salto de línea
^Inicio del string (o línea en modo MULTILINE)
$Fin del string (o línea en modo MULTILINE)
*Cero o más del elemento anterior
+Uno o más del elemento anterior
?Cero o uno del elemento anterior
{n}Exactamente n repeticiones
{n,m}Entre n y m repeticiones
[...]Clase de caracteres — cualquier carácter listado dentro
[^...]Clase negada — cualquier carácter no listado
|Alternación — la expresión izquierda o la derecha
()Grupo de captura
\Escapa un metacarácter o inicia una secuencia especial

Para hacer coincidir un metacarácter literal como . o *, escápalo con una barra invertida: \. coincide con un punto real.

Secuencias especiales

Las secuencias especiales son clases de caracteres abreviadas que aparecen con frecuencia en patrones del mundo real.

SecuenciaCoincide con
\dCualquier dígito — igual que [0-9]
\DCualquier no dígito
\wCarácter de palabra: letras, dígitos, guion bajo
\WCarácter que no es de palabra
\sEspacio en blanco: espacio, tabulación, salto de línea
\SNo espacio en blanco
\bLímite de palabra (anchura cero)
\BNo límite
import re

print(re.findall(r'\d+', 'I have 3 cats and 12 dogs'))
# ['3', '12']

print(re.findall(r'\w+', 'hello_world 123'))
# ['hello_world', '123']

print(re.findall(r'\bPython\b', 'Python Pythonista Python3'))
# ['Python']  — word boundary prevents partial matches

\b es especialmente útil: coincide con la posición entre un carácter de palabra y un carácter que no lo es, por lo que \bPython\b coincide con la palabra independiente "Python" pero no con "Pythonista" o "Python3".

Cuantificadores

Los cuantificadores controlan cuántas veces debe coincidir el elemento anterior.

import re

print(re.findall(r'a*', 'baaa'))   # ['', 'aaa', '']
print(re.findall(r'a+', 'baaa'))   # ['aaa']
print(re.findall(r'a?', 'baaa'))   # ['', 'a', 'a', 'a', '']
print(re.findall(r'a{3}', 'baaa')) # ['aaa']

Coincidencia codiciosa vs. perezosa

Por defecto, los cuantificadores son codiciosos — coinciden con la mayor cantidad de texto posible. Añade ? después del cuantificador para hacerlo perezoso (coincidir con la menor cantidad posible).

import re

html = '<b>bold</b> and <i>italic</i>'

print(re.findall(r'<.*>', html))
# ['<b>bold</b> and <i>italic</i>']  — greedy: matches from first < to last >

print(re.findall(r'<.*?>', html))
# ['<b>', '</b>', '<i>', '</i>']  — lazy: matches each individual tag

Los cuantificadores perezosos son esenciales cuando se analiza texto estructurado como HTML o fragmentos JSON.

Clases de caracteres

Una clase de caracteres [...] coincide con cualquier carácter del conjunto listado. Usa - para rangos y ^ al inicio para negar la clase.

import re

print(re.findall(r'[aeiou]', 'hello world'))
# ['e', 'o', 'o']

print(re.findall(r'[0-9]', 'a1b2c3'))
# ['1', '2', '3']

print(re.findall(r'[^aeiou\s]+', 'hello world'))
# ['h', 'll', 'w', 'rld']  — consonants only (not vowels, not spaces)

Rangos predefinidos comunes: [a-z] letras minúsculas, [A-Z] mayúsculas, [0-9] dígitos, [a-zA-Z0-9] alfanumérico.

Anclas

Las anclas no consumen caracteres — afirman una posición en el string.

import re

print(re.findall(r'^Python', 'Python is great'))
# ['Python']  — matches only if 'Python' is at the start

print(re.findall(r'great$', 'Python is great'))
# ['great']  — matches only if 'great' is at the end

print(re.findall(r'^Python', 'Learn Python'))
# []  — 'Python' is not at the start of this string

Consulta el flag re.MULTILINE más adelante en este capítulo para aplicar ^ y $ por línea en lugar de por string.

Alternación

La barra vertical | funciona como un OR lógico entre dos expresiones.

import re

print(re.findall(r'cat|dog', 'I have a cat and a dog'))
# ['cat', 'dog']

print(re.findall(r'colou?r|colour', 'color and colour'))
# ['color', 'colour']

Grupos de captura

Los paréntesis () crean un grupo de captura. Los grupos permiten extraer subpartes de una coincidencia. re.search() devuelve un objeto de coincidencia; llama a .group(n) o .groups() sobre él.

import re

match = re.search(r'(\d{4})-(\d{2})-(\d{2})', '2023-10-05')
if match:
    print(match.group(0))   # '2023-10-05'  — entire match
    print(match.group(1))   # '2023'
    print(match.groups())   # ('2023', '10', '05')

Grupos con nombre

Nombra un grupo con (?P<name>...) para acceder a él por nombre en lugar de por posición. Esto hace que los patrones sean mucho más fáciles de mantener.

import re

match = re.search(
    r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})',
    '2023-10-05'
)
if match:
    print(match.group('year'))   # '2023'
    print(match.groupdict())     # {'year': '2023', 'month': '10', 'day': '05'}

Grupos sin captura

Usa (?:...) cuando necesites agrupar pero no necesites capturar el valor.

import re

print(re.findall(r'(?:Mr|Mrs|Ms)\.? \w+', 'Mr. Smith and Mrs. Jones'))
# ['Mr. Smith', 'Mrs. Jones']

Lookahead y Lookbehind

El lookahead ((?=...)) y el lookbehind ((?<=...)) afirman que algo sigue o precede a la coincidencia, sin incluirlo en el resultado. Son de anchura cero: no consumen caracteres.

import re

# Positive lookahead — find numbers followed by 'dollars'
print(re.findall(r'\d+(?= dollars)', '100 dollars and 200 euros'))
# ['100']

# Negative lookahead — find numbers NOT followed by 'dollars'
print(re.findall(r'\b\d+\b(?! dollars)', '100 dollars and 200 euros'))
# ['200']

# Positive lookbehind — extract domain from email addresses
emails = 'Contact [email protected] or [email protected]'
print(re.findall(r'(?<=@)\w+\.\w+', emails))
# ['example.com', 'test.org']

Los patrones de lookbehind deben tener anchura fija en Python — no puedes usar * o + dentro de uno.

El módulo re: funciones principales

re.search() — Encontrar la primera coincidencia

Devuelve un objeto de coincidencia para la primera posición donde el patrón coincide, o None si no hay coincidencia.

import re

text = 'apple banana apple'
match = re.search(r'banana', text)
if match:
    print(match.group())   # 'banana'
    print(match.span())    # (6, 12)

re.match() — Coincidir solo al inicio

Similar a re.search(), pero el patrón debe coincidir en el principio del string.

import re

print(bool(re.match(r'\d+', 'abc123')))   # False — no digit at start
print(bool(re.search(r'\d+', 'abc123')))  # True  — digit found anywhere

Usa re.search() cuando quieras encontrar un patrón en cualquier lugar; usa re.match() cuando el patrón deba aparecer al inicio.

re.fullmatch() — Coincidir con el string completo

El patrón debe coincidir con todo el string de principio a fin.

import re

print(bool(re.fullmatch(r'\d{5}', '12345')))   # True  — exactly 5 digits
print(bool(re.fullmatch(r'\d{5}', '123456')))  # False — too long
print(bool(re.fullmatch(r'\d{5}', '1234X')))   # False — non-digit present

re.fullmatch() es ideal para la validación de entradas (códigos postales, números de teléfono, etc.).

re.findall() — Todas las coincidencias no solapadas

Devuelve una lista de todas las coincidencias. Si el patrón tiene grupos, devuelve una lista de tuplas.

import re

print(re.findall(r'\d+', 'abc 123 def 456'))
# ['123', '456']

# With a group — returns list of group values
print(re.findall(r'(\w+)@(\w+\.\w+)', '[email protected] [email protected]'))
# [('alice', 'example.com'), ('bob', 'test.org')]

re.finditer() — Iterador de objetos de coincidencia

Similar a re.findall(), pero devuelve objetos de coincidencia uno a la vez. Útil para textos grandes o cuando necesitas información de posición.

import re

for m in re.finditer(r'\d+', 'abc 123 def 456'):
    print(m.group(), m.start(), m.end())
# 123 4 7
# 456 12 15

re.sub() — Reemplazar coincidencias

Reemplaza cada ocurrencia del patrón con un string de reemplazo o el valor de retorno de una función.

import re

text = 'apple banana apple'
print(re.sub(r'apple', 'orange', text))
# 'orange banana orange'

# Limit replacements
print(re.sub(r'apple', 'orange', text, count=1))
# 'orange banana apple'

# Backreferences in replacement — reformat a date
print(re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3/\2/\1', '2023-10-05'))
# '05/10/2023'

re.split() — Dividir por un patrón

Divide el string en cada ocurrencia del patrón.

import re

print(re.split(r',\s*', 'apple, banana, cherry, date'))
# ['apple', 'banana', 'cherry', 'date']

# Limit the number of splits
print(re.split(r',\s*', 'apple, banana, cherry, date', maxsplit=2))
# ['apple', 'banana', 'cherry, date']

Compilar patrones con re.compile()

Cuando uses el mismo patrón muchas veces, compílalo una vez con re.compile() para mejorar el rendimiento. El objeto de patrón resultante tiene todos los mismos métodos (findall, search, sub, etc.).

import re

# Compile once
digit_pattern = re.compile(r'\d+')

# Reuse many times
print(digit_pattern.findall('abc 123 def 456'))   # ['123', '456']
print(digit_pattern.sub('NUM', 'abc 123 def 456')) # 'abc NUM def NUM'
print(digit_pattern.search('xyz 99'))              # <re.Match object ...>

La compilación es especialmente valiosa dentro de bucles o funciones que se llaman con frecuencia.

Flags

Los flags modifican el comportamiento de la coincidencia. Pásalos como último argumento a cualquier función de re, o inclúyelos al llamar a re.compile(). Se pueden combinar múltiples flags con |.

FlagForma cortaEfecto
re.IGNORECASEre.ICoincidencia sin distinguir mayúsculas/minúsculas
re.MULTILINEre.M^ y $ coinciden con el inicio/fin de cada línea
re.DOTALLre.S. también coincide con saltos de línea
re.VERBOSEre.XPermite espacios en blanco y comentarios dentro del patrón
import re

# IGNORECASE
print(re.findall(r'hello', 'Hello HELLO hello', re.IGNORECASE))
# ['Hello', 'HELLO', 'hello']

# MULTILINE — ^ matches the start of each line
text = 'first line\nsecond line\nthird line'
print(re.findall(r'^\w+', text, re.MULTILINE))
# ['first', 'second', 'third']

# DOTALL — . matches newline characters
print(re.findall(r'<.*?>', '<div>\n<p>text</p>\n</div>', re.DOTALL))
# ['<div>', '<p>', '</p>', '</div>']

# VERBOSE — write readable patterns with comments
date_pattern = re.compile(r'''
    (?P<year>\d{4})   # four-digit year
    -
    (?P<month>\d{2})  # two-digit month
    -
    (?P<day>\d{2})    # two-digit day
''', re.VERBOSE)
print(date_pattern.search('2023-10-05').groupdict())
# {'year': '2023', 'month': '10', 'day': '05'}

Escapar entradas del usuario con re.escape()

Si construyes un patrón a partir de texto proporcionado por el usuario, escápalo siempre primero para evitar la interpretación no intencionada de metacaracteres.

import re

user_input = 'hello.world'
# Without escaping, '.' matches any character
# With escaping, '\.' matches a literal dot
safe_pattern = re.escape(user_input)
print(safe_pattern)                                    # 'hello\\.world'
print(bool(re.search(safe_pattern, 'say hello.world')))  # True
print(bool(re.search(safe_pattern, 'say helloXworld')))  # False

Ejemplos prácticos

Validar una dirección de correo electrónico

import re

def is_valid_email(email):
    pattern = r'^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$'
    return bool(re.fullmatch(pattern, email))

print(is_valid_email('[email protected]'))   # True
print(is_valid_email('bad-email@'))         # False

Extraer todas las URLs de un texto

import re

text = 'Visit https://www.example.com or http://test.org for details.'
urls = re.findall(r'https?://[\w./-]+', text)
print(urls)
# ['https://www.example.com', 'http://test.org']

Eliminar espacios en blanco adicionales

import re

messy = '  hello    world   Python  '
clean = re.sub(r'\s+', ' ', messy).strip()
print(clean)  # 'hello world Python'

Analizar una línea de log

import re

log = '2023-10-05 14:32:11 ERROR Failed to connect to database'
pattern = re.compile(
    r'(?P<date>\d{4}-\d{2}-\d{2}) '
    r'(?P<time>\d{2}:\d{2}:\d{2}) '
    r'(?P<level>\w+) '
    r'(?P<message>.+)'
)
m = pattern.match(log)
if m:
    print(m.group('level'))    # 'ERROR'
    print(m.group('message'))  # 'Failed to connect to database'

Errores comunes

re.match() vs re.search()re.match() solo comprueba el inicio del string. Los nuevos usuarios a menudo esperan que busque en cualquier lugar y se confunden cuando devuelve None.

Olvidar los raw strings'\d' en un string normal de Python es simplemente 'd' con un escape no reconocido (o un SyntaxWarning en Python 3.12+). Escribe siempre r'\d'.

Las coincidencias codiciosas que capturan demasiado — Si un patrón .* captura más de lo que pretendes, cámbialo a .*? (perezoso).

Caracteres especiales en los strings de reemplazo — En re.sub(), las secuencias con barra invertida como \1 en el reemplazo hacen referencia a grupos capturados. Para incluir una barra invertida literal en el reemplazo, escribe \\.

re.findall() con grupos — Cuando tu patrón contiene grupos, re.findall() devuelve los grupos, no la coincidencia completa. Usa un grupo sin captura (?:...) si quieres la coincidencia completa.

Referencia rápida

TareaFunción
Encontrar la primera coincidenciare.search(pattern, text)
Encontrar todas las coincidenciasre.findall(pattern, text)
Iterar coincidenciasre.finditer(pattern, text)
Coincidir al iniciore.match(pattern, text)
Coincidir con el string completore.fullmatch(pattern, text)
Reemplazarre.sub(pattern, repl, text)
Dividirre.split(pattern, text)
Compilar para reutilizarre.compile(pattern)
Escapar entrada del usuariore.escape(text)

Capítulos relacionados

  • Python Strings — métodos de string que complementan regex para tareas de texto más simples.
  • Modify Strings — métodos integrados como replace() y split() que evitan regex cuando los patrones son fijos.
  • Python File Handling — leer archivos línea por línea para aplicar regex a gran escala.
  • Python Try/Except — manejar errores que surgen cuando los patrones regex se compilan a partir de entradas del usuario.
  • Python Functions — encapsular la lógica de regex en funciones reutilizables.

Práctica

Práctica
What does the 're' module in Python provide?
What does the 're' module in Python provide?
Was this page helpful?