Expresiones regulares a la vista
·
Resumen del curso
1
El pago desaparecido: buscar el texto tal cual
2
El coche que se dio a la fuga: clases de caracteres
3
La guía telefónica rota: cuantificadores
4
Los registros falsos: anclas y validación
5
Fechas por todas partes: grupos y alternativas
6
El acceso de las 3 de la madrugada: grupos con nombre y objetos Match
7
La víspera de la noticia: sustitución y división
8
El desafío del Backtracker: codicioso, perezoso y posesivo
9
Las reglas de la caja fuerte: lookaround
10
El mismo modus operandi: retrorreferencias y condicionales
11
La regex ilegible: flags y compilación
12
El Backtracker, desenmascarado: repaso y un tokenizador
CASE-003
Grep & Co. Agencia de Detectives de Datos
Expediente
Cliente
La dueña de la floristería «Flor Silvestre», en el mismo callejón
Pruebas
La lista de teléfonos de los clientes habituales, empapada por la lluvia y rota, pasada a texto con OCR. Cada número usa un separador distinto y se han colado cifras que no pintan nada.
Para cerrar el caso
Reunir todos los números de teléfono y todos los códigos postales de la lista.
Llegas por la mañana y la directora tiene en pantalla el patrón de matrícula de ayer. «En vez de escribir \d cuatro veces, se puede escribir "cuatro veces". Hoy aprendes eso». En ese momento se abre la puerta y entra la dueña de la floristería del callejón con un fajo de papeles mojados. «Se me ha empapado la libreta de teléfonos de los clientes de siempre. La escaneé y la pasé a texto, pero ha salido un desastre».
La lista leída con OCR es esta. Cada número usa un separador distinto, y la última línea tiene algo raro.
kim 010-0000-1234 04542
lee 010 0000 9876 04545
park 02-0000-5678 04538
choi 010.0000.4321 04539
yoon 1234-5678-9012La sintaxis que indica cuántas veces se repite algo se llama cuantificador. Hoy vas a poner orden en esta lista con cuantificadores.
Un cuantificador decide cuántas veces se repite el bloque que tiene justo delante. \d{4} es lo mismo que \d\d\d\d. Acortemos el patrón de matrícula de la lección 2.
re.findall(r"\d{2}[가-힣]\d{4}", "12가3457 123가4567") # ['12가3457', '23가4567']Es más corto, pero ya se ve un problema. Las matrículas nuevas, que salieron en 2019, empiezan con tres dígitos. De 123가4567 se ha comido el 1 inicial y solo se ha llevado 23가4567.
{m,n} significa entre m y n veces. Si cambias el principio por \d{2,3}, acepta tanto dos dígitos como tres.
re.findall(r"\d{2,3}[가-힣]\d{4}", "12가3457 123가4567") # ['12가3457', '123가4567']{2,3} no se ponen espacios alrededor de la coma. Si hay un espacio, deja de ser un cuantificador (mira el consejo de Dot, más abajo).\d{5}.Laboratorio de pruebas
re.search(r"", text)
Valor devuelto
En {m,n} se puede dejar vacío uno de los dos lados.
{m,} : m veces o más, sin límite por arriba{,n} : entre 0 y n veces. Como vale 0 veces, también coincide en un sitio donde no hay ningún carácterre.findall(r"a{2,}", "a aa aaaa") # ['aa', 'aaaa']
re.findall(r"\d{3,}", "1 22 333 4444") # ['333', '4444']
re.findall(r"\d{,2}", "123") # ['12', '3', '']El '' del final del último resultado es una coincidencia en el final de la cadena, donde hay cero dígitos. Estas coincidencias vacías las veremos con calma en el apartado 3-6.
Si la { no tiene forma de cuantificador, se trata como un carácter normal. a{x} busca los cuatro caracteres a, {, x y }.
re.findall(r"a{x}", "a{x} ax") # ['a{x}']
re.findall(r"a{2, 3}", "aa a{2, 3}") # ['a{2, 3}']El consejo de Dot
Puse un espacio después de la coma, a{2, 3}, y en vez de dar error se puso a buscar otra cosa, tan tranquilo. Si no tiene forma de cuantificador, lo toma como texto normal. Que no dé error no quiere decir que el patrón esté bien. Dentro de las llaves, solo números y comas.
Las cantidades más habituales tienen nombre de un solo carácter.
| Cuantificador | Significado | Equivale a |
|---|---|---|
* | 0 veces o más | {0,} |
+ | 1 vez o más | {1,} |
? | 0 o 1 vez (puede estar o no) | {0,1} |
re.findall(r"ab*", "a ab abbb") # ['a', 'ab', 'abbb']
re.findall(r"ab+", "a ab abbb") # ['ab', 'abbb']
re.findall(r"ab?", "a ab abbb") # ['a', 'ab', 'ab']
re.findall(r"colou?r", "color colour colouur") # ['color', 'colour']? va como anillo al dedo para «un carácter que puede estar o no». En la lista, los móviles usan separadores distintos: guion, espacio o punto. Si escribes la posición del separador como [-. ]?, vale cualquiera de los tres, o ninguno.
re.findall(r"010[-. ]?\d{4}[-. ]?\d{4}", phonebook)
# ['010-0000-1234', '010 0000 9876', '010.0000.4321']Como viste en la lección 2, dentro de los corchetes el . es un punto de verdad, y el - es literal porque va el primero.
Laboratorio de pruebas
re.search(r"", text)
Valor devuelto
El cuantificador se aplica solo al bloque que tiene justo delante. Un bloque es un carácter, una clase entre corchetes o una abreviatura como \d. Por eso ab+ no es «ab varias veces», sino «una a seguida de varias b».
re.findall(r"ab+", "abbb abab") # ['abbb', 'ab', 'ab']
re.fullmatch(r"ab+", "abab") # None
re.fullmatch(r"(ab)+", "abab") # <re.Match object; span=(0, 4), match='abab'>Para repetir varios caracteres a la vez, agrúpalos entre paréntesis y forma un solo bloque. (ab)+ es «ab una vez o más». Los paréntesis tienen otra función importante además de agrupar, pero esa la verás en la lección 5.
re.fullmatch solo coincide si la cadena entera encaja con el patrón. Las diferencias las veremos en detalle en la lección 4.
Laboratorio de pruebas
re.search(r"", text)
Valor devuelto
Por defecto, los cuantificadores son codiciosos (greedy): dentro de lo permitido, se llevan todo lo que pueden.
re.search(r"\d+", "tel 0100000") # <re.Match object; span=(4, 11), match='0100000'>
re.search(r"\d{2,4}", "123456") # <re.Match object; span=(0, 4), match='1234'>
re.findall(r"\d{2,4}", "123456") # ['1234', '56']\d{2,4} también aceptaría dos dígitos, pero primero se lleva cuatro. Solo si lo que viene después falla, va devolviendo de uno en uno. Este «devolver y volver a intentarlo» se llama backtracking, y en la lección 8 será lo que deje la agencia paralizada. El cuantificador perezoso +?, que se lleva lo menos posible, también lo verás entonces.
Ahora vamos a reunir lo que encontremos en toda la lista. Supongamos que la lista de arriba está en una cadena llamada phonebook.
re.findall devuelve el texto que coincide como una lista de cadenas. Así se comporta cuando el patrón no tiene paréntesis; qué pasa cuando los tiene lo veremos en la lección 5.
re.findall(r"\d{5}", phonebook) # ['04542', '04545', '04538', '04539']re.finditer devuelve un iterador que va entregando objetos Match uno a uno. Se usa cuando también necesitas la posición (span).
for m in re.finditer(r"\d{5}", phonebook):
print(m.group(), m.span())
# 04542 (22, 27)
# 04545 (50, 55)
# 04538 (78, 83)
# 04539 (106, 111)re.finditer.Aquí hay que tener cuidado con las coincidencias vacías. *, ? y {,n} permiten cero repeticiones, así que también tienen éxito en sitios donde no hay ni un carácter. findall mete esas coincidencias vacías en el resultado como ''.
re.findall(r"\d*", "a12b") # ['', '12', '', '']
re.findall(r"\d+", "a12b") # ['12']Ha salido una coincidencia vacía delante de la a, luego 12, otra vacía delante de la b y otra al final de la cadena. Cuando quieras reunir algo, usa + o {1,}, que no permiten cero repeticiones.
Laboratorio de pruebas
re.search(r"", text)
Valor devuelto
Un número de teléfono tiene un prefijo de 2 o 3 dígitos, un bloque central de 3 o 4 y un bloque final de 4. El separador puede estar o no. El código postal tiene cinco dígitos.
phones = re.findall(r"\d{2,3}[-. ]?\d{3,4}[-. ]?\d{4}", phonebook)
print(phones)
# ['010-0000-1234', '010 0000 9876', '02-0000-5678', '010.0000.4321', '234-5678-9012']
postcodes = re.findall(r"\d{5}", phonebook)
print(postcodes) # ['04542', '04545', '04538', '04539']Hemos reunido cuatro teléfonos y cuatro códigos postales. Pero ¿qué es ese quinto número, 234-5678-9012? La florista mira la pantalla y niega con la cabeza. «Eso no es un teléfono. Es una nota con mi número de tarjeta».
re.search(r"\d{2,3}[-. ]?\d{3,4}[-. ]?\d{4}", "yoon 1234-5678-9012")
# <re.Match object; span=(7, 20), match='234-5678-9012'>El patrón se ha saltado el primer carácter de 1234, el 1, y ha empezado a coincidir en la posición 7, en mitad de un número. Es el mismo accidente que el 23가4567 del apartado 3-1.
Laboratorio de pruebas
re.search(r"", text)
Valor devuelto
Informe de caso cerrado — Se recuperan de la lista OCR 4 teléfonos y 4 códigos postales, y se entregan a la dueña de la floristería. Queda sin resolver el problema de que parte de un número de tarjeta se tome por un teléfono.
La directora se queda un buen rato mirando la última línea. «Ha empezado en mitad del número. Este patrón no sabe dónde empieza ni dónde acaba un número». En la próxima lección aprenderás a escribir esos límites en el patrón.
\d{2,3}[-. ]?\d{3,4}[-. ]?\d{4} en 01000001234 (un número sin separadores) y luego ejecútalo.re.findall(r"\d?", "a1"). ¿Cuántas cadenas vacías habrá?re.finditer para encontrar los teléfonos de la lista e imprime, línea a línea, cada número con su posición inicial.La libreta de la directora
{m} es exactamente m veces; {m,n}, de m a n; {m,}, m o más; {,n}, de 0 a n. Nada de espacios junto a la coma.
* es 0 veces o más; +, 1 vez o más; ?, 0 o 1 vez.
El cuantificador solo afecta al bloque de justo delante. ab+ y (ab)+ no son lo mismo.
Los cuantificadores son codiciosos: se llevan todo lo que pueden.
re.findall devuelve una lista de cadenas; re.finditer, un iterador de Match.
Los patrones que permiten 0 repeticiones producen coincidencias vacías ''. Para reunir cosas, usa +.
← Anterior
Lección 2 · El coche que se dio a la fuga: clases de caracteres
Siguiente →
Lección 4 · Los registros falsos: anclas y validación
Recibe avisos de nuevas lecciones
Te avisaremos por correo cuando este curso tenga lecciones nuevas o se abra un curso de continuación.
R
ReflexKR
Aprende Python y Reflex haciendo. Todos los ejemplos se ejecutaron y verificaron en Reflex 0.10.0.
ReflexKR es gratis y sin banners publicitarios. Si te ayudó, puedes apoyarlo con un café. ☕ Invítanos un café
Aviso de afiliados© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
Preguntas y respuestas
Pregunta lo que quieras sobre esta lección. Aquí se reúnen las preguntas de todas las versiones de idioma de esta página.