# regex-con-python-desde-cero
Regex con Python desde Cero
Volver a la portada del cursoDesafío de Programación con Regex
Sección 4 · Aplicaciones Prácticas
Desafío de Programación con Regex
El notebook desafios-regex reúne diez problemas tomados de la bandeja de soporte y del log del servidor de PyShop, ordenados de menor a mayor dificultad. Cada desafío trae un enunciado, una función vacía para completar y una celda de pruebas con assert. Mientras la función no esté resuelta, la prueba imprime «pendiente» en lugar de detener el notebook, así que puedes avanzar en cualquier orden. Las soluciones están al final, en una sección separada: resuelve primero y compara después.
Cómo trabajar
- Ejecuta la primera celda de código: define
generar_tickets,generar_logyMESES, y la funciónprobarque corre las pruebas de cada desafío. - Lee el enunciado y escribe el patrón en la función. Prueba primero con los ejemplos cortos del enunciado.
- Ejecuta la celda de pruebas. Si sale «pendiente», imprime la coincidencia y su
span()antes de cambiar el patrón. - Cuando un desafío pase, ejecuta la línea que lo aplica a la bandeja completa y compara la cifra con la esperada.
La herramienta regex101, con el modo Python seleccionado, sirve para ver paso a paso qué coincide; el resultado que cuenta es el del notebook.
Los diez desafíos
| # | Desafío | Herramientas | Cifra esperada en la bandeja o el log |
|---|---|---|---|
| 1 | Pedidos: normalizar ps-12345 y PS 67890 a PS-ddddd | clase, ?, (?i), grupo | 600 pedidos, 597 distintos |
| 2 | Fechas: llevar los tres formatos a ISO | fullmatch, grupos, MESES | 488 fechas, 0 sin convertir |
| 3 | Teléfonos: dejar solo 10 dígitos | partes opcionales, {4} | 369 teléfonos |
| 4 | Montos: texto a float | sub, alternancia | 488 montos, suma 1,211,689.39 |
| 5 | Palabras repetidas: «favor favor» a «favor» | retrorreferencia \1, \b | 21 tickets corregidos |
| 6 | Rutas del log: /api/pedidos/243 a /api/pedidos/{id} | lookahead, sub | 404 más frecuente: /productos con 29 |
| 7 | Contraseñas: mayúscula, minúscula, dígito, símbolo y 10 caracteres | lookaheads, fullmatch | solo PyShop2026! pasa de los 4 casos |
| 8 | CSV simple: separar por coma o punto y coma sin espacios sobrantes | split, \s* | 4 campos limpios |
| 9 | Etiquetas: quitar las etiquetas HTML de una nota | clase negada | «Pedido y Factura» |
| 10 | Anonimizar: correos y teléfonos | sub, grupos, patrones previos | 0 correos y 0 teléfonos restantes |
Pistas por bloque
Desafíos 1 a 4 (extraer y normalizar). El pedido se resuelve con una sola regex: la bandera en línea (?i) cubre mayúsculas y minúsculas y [- ]? cubre el guion, el espacio o nada; el grupo guarda solo los cinco dígitos y el formato final se arma en Python. Las fechas conviene tratarlas con tres fullmatch, uno por formato, en lugar de un patrón gigante: el formato largo usa MESES.index(mes) + 1 para obtener el número del mes. En los montos, quitar el signo de pesos, las comas y « MXN» con un solo re.sub deja una cadena que float acepta.
Desafíos 5 y 6 (reemplazar con contexto). La retrorreferencia \1 repite lo que capturó el grupo 1. Sin \b, el patrón uniría «el el» dentro de «el elefante». En las rutas, el lookahead (?=/|$) asegura que el número sea un segmento completo de la ruta; así /buscar?q=audifonos queda intacta.
import re
def normaliza_ruta(ruta):
return re.sub(r"/\d+(?=/|$)", "/{id}", ruta)
assert normaliza_ruta("/api/pedidos/243") == "/api/pedidos/{id}"
assert normaliza_ruta("/productos/17") == "/productos/{id}"
assert normaliza_ruta("/buscar?q=audifonos") == "/buscar?q=audifonos"
Desafíos 7 a 9 (validar y limpiar). Cada condición de la contraseña es un lookahead independiente al inicio; la longitud va al final, con .{10,}. En el CSV, el espacio sobrante de 'transferencia ' aparece si el patrón solo consume espacios después del separador. Para las etiquetas, <[^>]+> no depende de codicioso o perezoso.
Desafío 10 (integrar). Reutiliza el patrón de correo y el de teléfono de los desafíos anteriores. El correo se reemplaza por ***@ más el dominio, para conservar el dato útil, y el teléfono por ** **** ****. La prueba final busca en la bandeja anonimizada cualquier correo o teléfono restante; deben quedar 0.
Qué revisar en las soluciones
- Que cada patrón sea raw string y no use
.*donde basta una clase negada. - Que las funciones devuelvan
Nonecuando el texto no tiene el dato, en lugar de lanzarAttributeError. - Que los patrones se definan una vez, compilados y con nombre, y se reutilicen entre desafíos.
- Que las pruebas incluyan casos que no deben coincidir, como
PS-482130o una fecha con mes 13.
Cierre
Los desafíos 1, 2, 3, 4 y 10 son las piezas del proyecto final: cuando pasen, ya tienes casi toda la limpieza de la bandeja. Guarda tus funciones en un módulo patrones.py y llévalas al proyecto con sus pruebas.