Regex parece que un gato caminó sobre el teclado, pero son cinco conceptos disfrazados de sintaxis densa. Aprende los cinco, guarda la biblioteca de patrones de abajo en favoritos, y conoce las dos trampas (matching codicioso, puntos sin escapar) que causan el 90% de los bugs de regex. El audio explica el razonamiento — los símbolos están aquí en la página cuando los necesites.
🎙️ Publicado y grabado:
Un regex describe la forma del texto: "tres dígitos, un guion, cuatro dígitos". El motor desliza esa descripción a lo largo de tu string buscando un lugar donde encaje. Ese es el mecanismo completo. Y la primera regla del regex: si buscas texto fijo, no necesitas regex — in / includes() es más rápido y no puede sorprenderte.
# forma: "dígitos, guion, dígitos" — matchea extensiones telefónicas
\d{3}-\d{4}
"call 555-0199 today" → matchea "555-0199"
# pero el texto fijo NO necesita regex:
✗ re.search("error", line) # innecesario
✓ "error" in line # mismo resultado, sin sorpresas
Una clase responde "¿qué caracteres pueden ocupar esta posición?" Tres atajos cubren la mayoría de casos — dígito, carácter de palabra, espacio en blanco — más corchetes para una lista personalizada de permitidos, y un circunflejo dentro para una lista de bloqueados.
\d # un dígito 0-9
\w # un carácter "palabra" letras, dígitos, _
\s # un espacio en blanco espacio, tab, salto de línea
. # CUALQUIER carácter (excepto salto de línea) — cuidado, ver 07
[abc] # exactamente uno de: a, b, o c
[a-f0-9] # un dígito hex (rangos con -)
[^0-9] # un carácter que NO sea dígito (^ dentro de [] = no)
\D \W \S # mayúsculas = lo opuesto a su gemela minúscula
Un cuantificador va después de un elemento y dice cuántas veces se repite. Cuatro cubren todo: opcional, cualquier cantidad, al menos uno, y conteos exactos entre llaves.
colou?r # ? = 0 o 1 → color, colour
go+al # + = 1 o más → goal, gooooal
ab*c # * = 0 o más → ac, abc, abbbc
\d{4} # exactamente 4 → 2026
\d{2,4} # 2 a 4 → 26, 202, 2026
\d{2,} # 2 o más
# práctica de lectura — la forma de una fecha simple:
\d{4}-\d{2}-\d{2} # 2026-07-22
Los 90 segundos más importantes de esta página. Los cuantificadores son codiciosos: agarran todo lo posible mientras el match siga funcionando. Combina eso con punto-asterisco y obtienes el bug más clásico de regex — un match que abarca desde la primera apertura hasta el último cierre, tragándose todo en medio.
text: <b>bold</b> and <i>italic</i>
<.*> # codicioso: matchea <b>bold</b> and <i>italic</i> — ¡TODO!
<.*?> # perezoso (añade ?): matchea <b>, luego </b>, luego <i>... ✓
<[^>]*> # a menudo mejor: "cualquier cosa excepto el cierre" — rápido y explícito
? después del cuantificador) o — generalmente mejor — reemplaza el punto con una clase negada: "cualquier cosa excepto el carácter de cierre". La clase negada dice lo que realmente quieres decir.
Las anclas matchean posiciones, no caracteres: inicio del string, final del string, borde de una palabra. Corrigen la clase escurridiza de bug donde tu patrón es correcto pero matchea en el lugar equivocado — validando "abc123abc" como número porque \d+ encontró el 123 en el medio.
^\d+$ # ^ inicio, $ final → el string ENTERO son dígitos
# sin ellos: "abc123abc" pasa! (encuentra 123 dentro)
\bcat\b # \b = borde de palabra → "cat" la palabra,
# no el cat en "category" o "concatenate"
^ERROR # líneas que EMPIEZAN con ERROR (con flag multilínea)
Los paréntesis hacen dos trabajos: agrupan (para que un cuantificador o alternancia aplique al bloque entero) y capturan (la parte matcheada regresa a tu código, numerada de izquierda a derecha). La alternancia — el pipe — significa "o".
# agrupar + o:
\.(jpg|png|gif)$ # archivos que terminan en cualquiera de los tres
# captura: extraer las partes DE una fecha
(\d{4})-(\d{2})-(\d{2})
# └ grupo 1: año └ 2: mes └ 3: día
# en Python:
m = re.search(r"(\d{4})-(\d{2})-(\d{2})", text)
m.group(1) # "2026"
# intercambiar con backreferences: "Apellido, Nombre" → "Nombre Apellido"
re.sub(r"(\w+), (\w+)", r"\2 \1", "Lovelace, Ada")
Quince caracteres tienen significados especiales. Usa uno literalmente y olvida la barra invertida, y tu patrón silenciosamente matchea más de lo que querías — el clásico es el punto sin escapar en dominios y precios, que significa "cualquier carácter".
# caracteres especiales (escapar con \ para usarlos literalmente):
. * + ? ( ) [ ] { } ^ $ | \ /
swiftgrasp.com # el . matchea CUALQUIER carácter:
# también matchea "swiftgraspXcom" ⚠
swiftgrasp\.com # ✓ un punto literal
$19.99 # $ significa "final del string" — ¡nunca matchea!
\$19\.99 # ✓ dólar literal, punto literal
swiftgrasp.com sí matchea "swiftgrasp.com". Tus tests pasan. Solo que también matchea cosas que no debería, y te enteras en producción, por un caso borde raro. El sobre-matching silencioso es la razón por la que los regex necesitan tests negativos: asegura lo que no debe matchear.
Los patrones que todos realmente necesitan, probados y anotados. Una nota honesta primero: el "regex perfecto para email" es una trampa famosa — la especificación permite monstruosidades, así que los sistemas en producción usan un patrón simple más un email de confirmación. El pragmatismo le gana al purismo de especificación.
# email (pragmático — combinar con un mail de confirmación)
^[\w.+-]+@[\w-]+\.[\w.]+$
# URL (http/https)
https?://[^\s]+
# fecha ISO 2026-07-22
\b\d{4}-\d{2}-\d{2}\b
# hora 14:30 o 9:05
\b\d{1,2}:\d{2}\b
# número con decimales opcionales (precios, montos)
-?\d+(\.\d+)?
# IPv4 (pragmático)
\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b
# colapsar espacios repetidos → un solo espacio
\s+ # reemplazar con " "
# extraer todo entre comillas
"([^"]*)"
# color hex #fff o #1a2b3c
#[0-9a-fA-F]{3,6}\b
La habilidad más senior de regex es declinar usarlo. Tres reglas honestas: el texto fijo no necesita regex; las estructuras anidadas (HTML, JSON, código) no se pueden parsear con regex — eso es un límite matemático, no un problema de habilidad — usa un parser real; y si el patrón no cabe en una línea, tu yo del futuro maldecirá a tu yo del presente. Regex es un bisturí, no una motosierra.
✗ parsear HTML con regex # usa un parser HTML (BeautifulSoup...)
✗ parsear JSON con regex # usa json.loads — existe, está ahí mismo
✗ un regex de 200 caracteres # divídelo en pasos, o escribe un mini parser
✓ extracción de líneas de log # el terreno natural de regex
✓ validación (¡anclada!) # con tests negativos
✓ buscar y reemplazar en editor # superpoder diario
Todo lo anterior, comprimido.
# clases # cuantificadores
\d dígito [abc] uno de ? 0-1 * 0+
\w palabra [a-z] rango + 1+ {n,m} contado
\s espacio [^x] no x añadir ? después → versión perezosa
# anclas # grupos
^ inicio $ final (x) captura
\b borde de palabra (a|b) o
validar = ^...$ siempre \1 backreference
# las dos trampas
.* demasiado codicioso → .*? o [^X]*
literal . $ + ? → escapar: \. \$ \+ \?
# depurar
regex101.com — explica, prueba, guarda
# declinar amablemente
texto fijo → in/includes · HTML/JSON → parser real
Eso completa el stack de desarrollador principiante: Python → Git → línea de comandos → SQL → HTTP → Docker → regex. Siete páginas, unos setenta minutos de audio, y un vocabulario funcional para tu primer año construyendo cosas. Ve y construye algo.