Inicio / Python / Python: Desde Cero hasta Profesional / Expresiones Regulares

Expresiones Regulares

Módulo re, match/search/findall/sub, grupos y flags.

Avanzado
🔒 Solo lectura
📖

Estás en modo lectura

Puedes leer toda la lección, pero para marcar progreso, hacer ejercicios y ganar XP necesitas una cuenta Pro.

Desbloquear por $9/mes


title: "Expresiones Regulares" slug: "python-expresiones-regulares" description: "Domina el módulo re de Python para buscar, validar y transformar texto usando patrones de expresiones regulares."

Expresiones Regulares

Las expresiones regulares (regex) son patrones de texto que permiten buscar, validar y transformar cadenas de forma potente y flexible. Python incluye el módulo re en su biblioteca estándar, proporcionando todas las herramientas necesarias para trabajar con regex de manera eficiente.

Introducción al Módulo re

import re

# Búsqueda básica
texto = "Mi número es 555-1234"
resultado = re.search(r"\d{3}-\d{4}", texto)

if resultado:
    print(resultado.group())  # 555-1234
    print(resultado.start())  # 14 (posición de inicio)
    print(resultado.end())    # 22 (posición de fin)
    print(resultado.span())   # (14, 22)

Nota: Siempre usa cadenas crudas (r"...") para patrones regex. Esto evita conflictos con las secuencias de escape de Python.

Funciones Principales

re.match() — Coincidencia al Inicio

Solo busca al inicio de la cadena:

import re

# match busca SOLO al inicio
print(re.match(r"\d+", "123abc"))    # Match: '123'
print(re.match(r"\d+", "abc123"))    # None (no empieza con dígitos)

re.search() — Primera Coincidencia

Busca la primera coincidencia en cualquier posición:

import re

texto = "Hoy es 23 de febrero de 2026"
m = re.search(r"\d+", texto)
print(m.group())  # "23" — primera coincidencia

re.findall() — Todas las Coincidencias

Devuelve una lista con todas las coincidencias:

import re

texto = "Precios: $15.99, $23.50 y $8.00"
precios = re.findall(r"\$\d+\.\d{2}", texto)
print(precios)  # ['$15.99', '$23.50', '$8.00']

# Con grupos, devuelve tuplas
pares = re.findall(r"(\w+)=(\w+)", "color=rojo tamaño=grande")
print(pares)  # [('color', 'rojo'), ('tamaño', 'grande')]

re.finditer() — Iterador de Coincidencias

Similar a findall, pero devuelve un iterador de objetos Match, dando acceso a más información:

import re

texto = "Error en línea 42, otro error en línea 87"
for m in re.finditer(r"línea (\d+)", texto):
    print(f"Encontrado '{m.group()}' en posición {m.start()}")
    print(f"  Número de línea: {m.group(1)}")
# Encontrado 'línea 42' en posición 10
#   Número de línea: 42
# Encontrado 'línea 87' en posición 33
#   Número de línea: 87

re.sub() — Reemplazar

Reemplaza todas las coincidencias del patrón:

import re

# Reemplazo simple
texto = "Hola   mundo,   esto   tiene   espacios"
limpio = re.sub(r"\s+", " ", texto)
print(limpio)  # "Hola mundo, esto tiene espacios"

# Reemplazo con función
def censurar(match):
    return "*" * len(match.group())

texto = "Mi contraseña es abc123 y mi PIN es 9876"
censurado = re.sub(r"\b[a-zA-Z0-9]{4,}\b", censurar, texto)
print(censurado)  # "Mi ********** es ****** y mi PIN es ****"

# Reemplazo con referencia a grupos
texto = "García, Ana"
invertido = re.sub(r"(\w+), (\w+)", r"\2 \1", texto)
print(invertido)  # "Ana García"

re.split() — Dividir por Patrón

import re

# Dividir por múltiples delimitadores
texto = "uno;dos,tres:cuatro cinco"
partes = re.split(r"[;,:\s]+", texto)
print(partes)  # ['uno', 'dos', 'tres', 'cuatro', 'cinco']

# Limitar divisiones
partes = re.split(r"[;,:\s]+", texto, maxsplit=2)
print(partes)  # ['uno', 'dos', 'tres:cuatro cinco']

Metacaracteres y Cuantificadores

Metacaracteres Básicos

Patrón Significado
. Cualquier carácter excepto \n
^ Inicio de cadena (o línea con MULTILINE)
$ Fin de cadena (o línea con MULTILINE)
\d Dígito [0-9]
\D No dígito [^0-9]
\w Alfanumérico [a-zA-Z0-9_]
\W No alfanumérico
\s Espacio en blanco [ \t\n\r\f\v]
\S No espacio en blanco
\b Límite de palabra

Cuantificadores

Patrón Significado
* 0 o más (greedy)
+ 1 o más (greedy)
? 0 o 1
{n} Exactamente n
{n,m} Entre n y m
*?, +? Versiones no-greedy (lazy)
import re

# Greedy vs Lazy
html = "<b>texto</b> y <i>otro</i>"
print(re.findall(r"<.+>", html))   # ['<b>texto</b> y <i>otro</i>'] (greedy)
print(re.findall(r"<.+?>", html))  # ['<b>', '</b>', '<i>', '</i>'] (lazy)

Grupos

Grupos de Captura

Los paréntesis () crean grupos de captura que extraen partes del match:

import re

fecha = "Fecha: 2026-02-23"
m = re.search(r"(\d{4})-(\d{2})-(\d{2})", fecha)

print(m.group())   # "2026-02-23" (match completo)
print(m.group(1))  # "2026" (primer grupo)
print(m.group(2))  # "02"
print(m.group(3))  # "23"
print(m.groups())  # ('2026', '02', '23')

Grupos con Nombre (Named Groups)

Más legibles que los grupos numéricos:

import re

patron = r"(?P<año>\d{4})-(?P<mes>\d{2})-(?P<dia>\d{2})"
m = re.search(patron, "Hoy es 2026-02-23")

print(m.group("año"))  # "2026"
print(m.group("mes"))  # "02"
print(m.group("dia"))  # "23"
print(m.groupdict())   # {'año': '2026', 'mes': '02', 'dia': '23'}

Grupos No Capturantes

Cuando necesitas agrupar sin capturar, usa (?:...):

import re

# Sin capturar el grupo
urls = re.findall(r"https?://(?:www\.)?(\w+\.\w+)", 
                  "Visita https://www.python.org o http://docs.python.org")
print(urls)  # ['python.org', 'python.org']

Flags (Banderas)

Las flags modifican el comportamiento del motor regex:

import re

# IGNORECASE — ignorar mayúsculas/minúsculas
m = re.search(r"python", "Me gusta PYTHON", re.IGNORECASE)
print(m.group())  # "PYTHON"

# MULTILINE — ^ y $ coinciden con inicio/fin de cada línea
texto = """Primera línea
Segunda línea
Tercera línea"""
inicios = re.findall(r"^\w+", texto, re.MULTILINE)
print(inicios)  # ['Primera', 'Segunda', 'Tercera']

# DOTALL — el punto (.) también coincide con \n
texto_ml = "Inicio\nContenido\nFin"
m = re.search(r"Inicio(.+)Fin", texto_ml, re.DOTALL)
print(m.group(1))  # "\nContenido\n"

# Combinar flags con |
m = re.search(r"^inicio(.+)fin$", "INICIO\ntexto\nFIN",
              re.IGNORECASE | re.DOTALL | re.MULTILINE)

# VERBOSE — permitir comentarios y espacios (mejora legibilidad)
patron_email = re.compile(r"""
    ^                   # Inicio de la cadena
    [a-zA-Z0-9._%+-]+   # Usuario (letras, dígitos, puntos, etc.)
    @                   # Arroba
    [a-zA-Z0-9.-]+      # Dominio
    \.[a-zA-Z]{2,}      # Extensión (.com, .org, etc.)
    $                   # Fin de la cadena
""", re.VERBOSE)

Compilar Patrones

Si usas un patrón repetidamente, compílalo para mejorar el rendimiento:

import re

# Compilar patrón (recomendado para uso repetido)
patron_telefono = re.compile(r"\+?\d{1,3}[\s-]?\d{3}[\s-]?\d{3}[\s-]?\d{3}")

textos = [
    "Llama al +34 600 123 456",
    "Mi número: 600-789-012",
    "Contacto: +1 555 867 5309",
]

for texto in textos:
    m = patron_telefono.search(texto)
    if m:
        print(f"Teléfono encontrado: {m.group()}")

Ejemplos Prácticos

Validar un Email

import re

def validar_email(email):
    patron = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
    return bool(re.match(patron, email))

print(validar_email("usuario@ejemplo.com"))   # True
print(validar_email("invalido@.com"))          # False

Extraer URLs de un Texto

import re

def extraer_urls(texto):
    patron = r"https?://[^\s<>\"']+|www\.[^\s<>\"']+"
    return re.findall(patron, texto)

texto = """
Visita https://python.org para más info.
Documentación en https://docs.python.org/3/library/re.html
"""
print(extraer_urls(texto))

Limpiar y Normalizar Texto

import re

def normalizar_texto(texto):
    """Limpia y normaliza un texto."""
    texto = re.sub(r"<[^>]+>", "", texto)         # Eliminar HTML
    texto = re.sub(r"[^\w\sáéíóúñÁÉÍÓÚÑ]", "", texto)  # Solo alfanuméricos
    texto = re.sub(r"\s+", " ", texto)             # Espacios múltiples
    return texto.strip().lower()

html = "<p>¡Hola   <b>Mundo</b>!  Esto es una   prueba.</p>"
print(normalizar_texto(html))  # "hola mundo esto es una prueba"

Ejercicio Práctico

Crea un validador de formularios usando expresiones regulares:

import re

def validar_formulario(datos):
    """Valida un diccionario de datos de formulario."""
    errores = []
    
    # Validar nombre (solo letras y espacios, 2-50 caracteres)
    if not re.match(r"^[a-zA-ZáéíóúñÁÉÍÓÚÑ\s]{2,50}$", datos.get("nombre", "")):
        errores.append("Nombre inválido")

    # Validar email
    if not re.match(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
                    datos.get("email", "")):
        errores.append("Email inválido")

    # Validar teléfono (formato español)
    if not re.match(r"^\+?34?\s?\d{3}\s?\d{3}\s?\d{3}$",
                    datos.get("telefono", "")):
        errores.append("Teléfono inválido")

    # Validar contraseña (mínimo 8 chars, mayúscula, minúscula, dígito)
    pwd = datos.get("password", "")
    if len(pwd) < 8:
        errores.append("Contraseña muy corta")
    elif not re.search(r"[A-Z]", pwd):
        errores.append("Contraseña sin mayúscula")
    elif not re.search(r"[a-z]", pwd):
        errores.append("Contraseña sin minúscula")
    elif not re.search(r"\d", pwd):
        errores.append("Contraseña sin dígito")

    return errores if errores else "Formulario válido"


# Prueba
datos = {
    "nombre": "Ana García",
    "email": "ana@ejemplo.com",
    "telefono": "+34 600 123 456",
    "password": "MiClave123",
}
print(validar_formulario(datos))  # "Formulario válido"

Reto: Crea un parser de logs que extraiga la fecha, nivel (INFO/ERROR/WARNING), y mensaje de líneas con formato [2026-02-23 14:30:00] ERROR: Mensaje aquí.

Resumen

  • El módulo re proporciona match, search, findall, finditer, sub y split.
  • Los metacaracteres (\d, \w, \s, ., ^, $) y cuantificadores (*, +, ?, {n,m}) forman los bloques de los patrones.
  • Los grupos () capturan partes del match; los named groups (?P<nombre>...) mejoran la legibilidad.
  • Las flags como IGNORECASE, MULTILINE, DOTALL y VERBOSE modifican el comportamiento del patrón.
  • Compila patrones que uses repetidamente con re.compile() para mejor rendimiento.
  • Usa siempre cadenas raw (r"...") al definir patrones regex.
🔒

Ejercicio práctico disponible

Regex en acción

Desbloquear ejercicios
// Regex en acción
// Desbloquea Pro para acceder a este ejercicio
// y ganar +50 XP al completarlo

function ejemplo() {
    // Tu código aquí...
}

¿Te gustó esta lección?

Con Pro puedes marcar progreso, hacer ejercicios, tomar quizzes, ganar XP y obtener tu constancia.

Ver planes desde $9/mes