Saltar al contenido

# regex-con-python-desde-cero

Regex con Python desde Cero

Volver a la portada del curso
Lectura (Markdown)

Uso de Biblioteca re en Python

Sección 3 · Regex Avanzado en Python

Uso de Biblioteca re en Python

Un patrón correcto puede dar un resultado incorrecto si se usa con la función equivocada. Sobre la frase «Pedido PS-48213 y PS-10077», re.match(r"PS-\d{5}", …) devuelve None y re.search devuelve PS-48213, con el mismo patrón. La diferencia está en la API del módulo re. Esta lectura la recorre completa: buscar, extraer, reemplazar, dividir, compilar y manejar errores.

1. Buscar: match, search y fullmatch

  • re.match(p, s) solo prueba en la posición 0. Por eso falla con «Pedido PS-48213…»: el texto empieza con «Pedido».
  • re.search(p, s) recorre la cadena y devuelve la primera coincidencia.
  • re.fullmatch(p, s) exige que el patrón cubra la cadena completa. Es la función para validar: fullmatch(r"PS-\d{5}", "PS-48213") coincide y con "PS-482130" devuelve None, aunque search sí encontraría algo.

Las tres devuelven un objeto Match o None, así que se usan directamente en un if. El objeto Match ofrece group(), groups(), groupdict(), start(), end(), span() y lastgroup, el nombre del último grupo con nombre que coincidió.

2. Extraer: findall y finditer

findall devuelve una lista de cadenas: ['PS-48213', 'PS-10077']. Si el patrón tiene grupos, cambia de forma: con un grupo devuelve solo ese grupo y con varios, tuplas. finditer devuelve un iterador de objetos Match, útil cuando se necesitan posiciones o grupos con nombre:

import re

s = "Pedido PS-48213 y PS-10077"
print(re.match(r"PS-\d{5}", s))           # None
print(re.search(r"PS-\d{5}", s).group())  # PS-48213
print(re.findall(r"PS-\d{5}", s))         # ['PS-48213', 'PS-10077']
print([m.span() for m in re.finditer(r"PS-\d{5}", s)])
# [(7, 15), (18, 26)]
fecha = r"(?P<iso>\d{4}-\d{2}-\d{2})|(?P<dmy>\d{2}/\d{2}/\d{4})"
m = re.search(fecha, "el 28/02/2026")
print(m.lastgroup, m.span())              # dmy (3, 13)

Con un lote grande, finditer además evita construir la lista completa en memoria.

3. Reemplazar: sub y subn

re.sub(patron, reemplazo, texto) reemplaza todas las coincidencias. El reemplazo puede ser una plantilla con referencias a grupos, \1 o \g<nombre>, o una función que recibe cada Match y devuelve el texto nuevo. La función permite lógica que una plantilla no expresa, como conservar solo la inicial de un correo:

dmy = r"(?P<d>\d{2})/(?P<m>\d{2})/(?P<a>\d{4})"
print(re.sub(dmy, r"\g<a>-\g<m>-\g<d>", "04/03/2026"))  # 2026-03-04

def ocultar(m):
    return m.group(1)[0] + "***@"

print(re.sub(r"([\w.]+)@", ocultar, "ana.lopez@correo.com"))
# a***@correo.com
print(re.subn(r"\d", "#", "Tel 55 1234"))  # ('Tel ## ####', 6)

subn devuelve una tupla con el texto y el número de reemplazos: sirve para registrar cuántos datos se anonimizaron.

4. Dividir: split

re.split(r"[;,]\s*", "tarjeta, efectivo;transferencia ,PayPal") devuelve ['tarjeta', 'efectivo', 'transferencia ', 'PayPal']. El espacio que queda en 'transferencia ' estaba antes de la coma y el patrón solo consumía espacios después; \s*[;,]\s* limpia ambos lados. Si el patrón tiene un grupo, los separadores también aparecen en el resultado: re.split(r"([;,])", "a,b;c") da ['a', ',', 'b', ';', 'c'].

5. Compilar, banderas y errores

re.compile devuelve un objeto Pattern con los mismos métodos (p.search, p.sub…). El módulo guarda en caché los patrones recientes, así que compilar no es obligatorio para la velocidad. En la máquina donde se preparó el curso, 200 pasadas de 600 búsquedas tardaron 0.044 s con re.search y una cadena, y 0.023 s con el patrón compilado: se ahorra la consulta a la caché en cada llamada. La razón principal para compilar es otra: dar nombre al patrón y definirlo una sola vez.

Las banderas se pasan como argumento con nombre (flags=re.IGNORECASE | re.MULTILINE) o en línea al inicio del patrón: (?i)ps-\d{5} encuentra ps-20458. Desde Python 3.13, pasar count, maxsplit o flags como posicionales en sub, subn y split está obsoleto; escríbelos con nombre. Un patrón mal escrito lanza re.error, que en Python 3.13 se llama re.PatternError (el nombre viejo sigue funcionando). Por ejemplo, (?<=\$\d+) falla con «look-behind requires fixed-width pattern».

Trampas comunes

  • match para buscar. Solo mira el inicio; para validar usa fullmatch y para buscar, search.
  • Llamar .group() sin comprobar. Si no hay coincidencia, None.group() lanza AttributeError.
  • Grupos que cambian findall. Agregar paréntesis convierte la lista de cadenas en grupos o tuplas; usa (?:…) si solo agrupas.
  • \1 sin raw string en el reemplazo. "\1" es el carácter de código 1; escribe r"\1".
  • Banderas posicionales. re.sub(p, r, s, re.I) pasa la bandera como count: usa flags=re.I.

Cierre

En el notebook practica-avanzada se aplica esta API a la bandeja: validadores con fullmatch, anonimización con sub y una función, y posiciones con finditer. Ante cada tarea, elige primero la función (validar, buscar, extraer, reemplazar o dividir) y después escribe el patrón.