Saltar al contenido

# regex-con-python-desde-cero

Regex con Python desde Cero

Volver a la portada del curso
Lectura (Markdown)

Sintaxis de Regex

Sección 1 · Introducción a Regex

Sintaxis de Regex

La bandeja de soporte de PyShop tiene 600 tickets y 97,737 caracteres. El patrón PS-\d{5} encuentra el pedido en 469 de ellos; los otros 131 lo escriben como ps-20458 o PS 12345. Para llegar a 600 no hace falta más código, sino más vocabulario: clases, atajos, anclas y banderas. Esta lectura es la tabla de referencia de ese vocabulario, con un ejemplo real de la bandeja para cada pieza.

1. Literales y metacaracteres

En una expresión regular casi todos los caracteres son literales: P busca una P y - busca un guion. Solo 14 caracteres tienen un significado especial:

MetacarácterSignificadoEjemplo
.cualquier carácter salvo el salto de líneaP. en «PS» → «PS»
^ y $inicio y fin de la cadena (o de cada línea con re.MULTILINE)^Ticket
* + ?cero o más, una o más, cero o una repeticiones\d+
{ }repetición exacta o en rango\d{5}
[ ]clase de caracteres: uno de los listados[Pp]
\escape o atajo\., \d
|alternancia: una opción u otraPS|ps
( )grupo(\d{5})

Las repeticiones y los grupos tienen sus propias lecciones; aquí interesa reconocerlos para saber cuándo escaparlos.

2. Escapes, raw strings y re.escape

Para buscar un metacarácter como literal se antepone una barra invertida. El caso típico es el punto decimal: en «Total 1249.50 o 1249x50», el patrón \d+.\d+ devuelve ['1249.50', '1249x50'], porque el punto acepta cualquier carácter; \d+\.\d+ devuelve solo ['1249.50'].

Las barras invertidas chocan con las de Python: en una cadena normal "\n" es un salto de línea (longitud 1), mientras que r"\n" son dos caracteres, la barra y la n (longitud 2). Por eso los patrones se escriben siempre como raw strings r"…": el texto llega intacto al módulo re. Si el patrón sale de un texto fijo, como un monto, re.escape escapa todo lo necesario:

import re

print(len("\n"), len(r"\n"))     # 1 2
print(re.escape("$1,249.50"))     # \$1,249\.50
texto = "Total 1249.50 o 1249x50"
print(re.findall(r"\d+.\d+", texto))   # ['1249.50', '1249x50']
print(re.findall(r"\d+\.\d+", texto))  # ['1249.50']

3. Clases, rangos y negación

Una clase […] coincide con un carácter de los listados. [Pp][Ss][- ]\d{5} acepta mayúscula o minúscula y guion o espacio: encuentra el pedido en los 600 tickets. Dentro de la clase se pueden usar rangos ([0-9], [a-z]) y, si el primer carácter es ^, la clase se niega: [^\d\s] significa «ni dígito ni espacio», y en «PS-4821 3» devuelve ['P', 'S', '-']. Dentro de una clase casi todos los metacaracteres pierden su significado: [.] es un punto literal.

4. Atajos y Unicode

AtajoEquivale aNegado
\ddígito\D
\wcarácter de palabra: letra, dígito o guion bajo\W
\sespacio, tabulador, salto de línea\S

Con patrones de tipo str, Python usa Unicode por defecto: \w+ en «Sofía pagó 899» devuelve ['Sofía', 'pagó', '899']. Con la bandera re.ASCII, \w se reduce a [a-zA-Z0-9_] y el mismo texto se rompe en ['Sof', 'a', 'pag', '899']. En texto en español conviene dejar Unicode.

5. Anclas y re.MULTILINE

Las anclas no consumen caracteres: marcan posiciones. ^ y $ son el inicio y el fin; \A y \Z son el inicio y el fin de la cadena completa, sin importar banderas; \b es un borde de palabra y \B su negación. En «favorito favor», favor coincide 2 veces y \bfavor\b solo 1, porque «favorito» sigue con letras.

La bandeja unida en una sola cadena tiene 600 líneas que dicen Ticket T-0001, Ticket T-0002… El patrón ^Ticket T-\d{4}$ encuentra 0, porque sin banderas ^ solo es el inicio del texto y $ el final. Con re.MULTILINE ambos se aplican a cada línea y encuentra 600:

bandeja = "\n\n".join(generar_tickets())
patron = r"^Ticket T-\d{4}$"
print(len(re.findall(patron, bandeja)))                # 0
print(len(re.findall(patron, bandeja, re.MULTILINE)))  # 600

6. El punto y re.DOTALL

El punto no cruza saltos de línea. En el primer ticket, Ticket.+De no encuentra nada, porque «Ticket» y «De» están en líneas distintas. Con re.DOTALL (alias re.S) el punto acepta también \n y Ticket.+?De coincide una vez. Las banderas se combinan con |: re.IGNORECASE | re.MULTILINE.

Trampas comunes

  • Olvidar la r. "\b" sin raw string es un retroceso (backspace), no un borde de palabra, y el patrón deja de coincidir sin dar error.
  • Punto sin escapar. \d+.\d+ acepta «1249x50». Para el separador decimal usa \..
  • Confundir ^ dentro y fuera de la clase. En ^PS es ancla; en [^PS] es negación.
  • Esperar que ^ funcione por línea. Sin re.MULTILINE, solo marca el inicio del texto completo.
  • Usar re.ASCII con texto en español. Parte las palabras con acentos.

Cierre

En el notebook fundamentos-y-sintaxis se recorre esta tabla sobre la bandeja completa: del 469 inicial al 600 con una clase y luego con re.IGNORECASE. Antes de escribir un patrón nuevo, pruébalo en un texto con un caso que debe coincidir y otro que no; el segundo es el que detecta los errores.

Recursos