Una expresión regular es un «lenguaje de patrones»
Una expresión regular describe con un pequeño juego de símbolos «cómo es una clase de cadenas». Las letras y los dígitos suelen representarse a sí mismos, mientras que una docena de metacaracteres (. * + ? ^ $ | ( ) [ ] { } \) tienen significados especiales. El motor recorre el texto de izquierda a derecha con el patrón y prueba a coincidir en cada posición; cuando lo consigue, informa de la posición y la longitud. Este curso usa el dialecto de JavaScript (ECMAScript), cuya sintaxis básica es casi idéntica a la de Python, Java o Go y cuyas diferencias se concentran en los lookarounds, las propiedades Unicode y algunos flags.
Niveles 1–3: literales, punto y escapes
cat coincide con la secuencia de las tres letras c, a y t. El punto . coincide con cualquier carácter menos el salto de línea, así que c.t encuentra cat, cut y c9t. Cuando de verdad quieres encontrar un punto (por ejemplo, el de delante de una extensión de archivo), hay que escribir \.: la barra invertida devuelve al metacarácter su sentido literal. Todos los metacaracteres se pueden escapar así; dentro de una clase de caracteres solo hay que escapar ] \ ^ -.
Niveles 4–7: clases de caracteres
Los corchetes enumeran caracteres candidatos: [aeiou] coincide con cualquier vocal; [a-z] y [0-9] usan el guion para los rangos y se pueden encadenar en [A-Za-z0-9_]; un ^ al principio niega la clase, y [^0-9] coincide con cualquier cosa que no sea un dígito. Las tres clases predefinidas habituales son abreviaturas de esto:
| Clase |
Equivale a |
Descripción |
\d |
[0-9] |
dígito (no incluye números chinos ni dígitos de ancho completo) |
\w |
[A-Za-z0-9_] |
«carácter de palabra»; ojo, los caracteres chinos no cuentan |
\s |
[ \t\n\r\f\v…] |
espacio en blanco, incluido el espacio de ancho completo |
Las versiones mayúsculas \D \W \S son sus negaciones. Que \w no incluya los caracteres chinos es la trampa más habitual: para encontrarlos hay que escribir [\u4e00-\u9fa5] o, con el flag u, usar \p{Script=Han}.
Niveles 8–10: cuantificadores
Un cuantificador va detrás de un átomo (un carácter, una clase o un grupo) y dice cuántas veces se repite:
* cero o más veces, + una o más veces, ? cero o una vez;
{n} exactamente n veces, {n,} al menos n veces, {n,m} de n a m veces.
\d{4} se lee mejor que \d\d\d\d, y \d{11} son los dígitos de un móvil. El cuantificador solo afecta al átomo que tiene justo delante: ab+ es una a seguida de una o más b; para repetir el ab completo hay que escribir (ab)+.
Niveles 11–12: anclas y límites
^ y $ no coinciden con caracteres, sino con posiciones: el inicio y el final de la cadena (con el modo multilínea, de cada línea). En una validación de formulario casi siempre hay que escribir ^…$, porque si no \d{11} «encuentra» los 11 primeros dígitos de un número de 12. \b es el límite de palabra: la posición entre \w y \W (o los extremos de la cadena). \bcat\b encuentra a cat pero no concatenate. Y como \w no incluye los caracteres chinos, \b apenas sirve con texto chino.
Niveles 13–17: alternancia, grupos y referencias
| significa «o» y tiene la prioridad más baja: cat|dog coincide con una de las dos palabras, y para limitar su alcance hay que usar paréntesis, gr(a|e)y. Los paréntesis crean además un grupo de captura: tras una coincidencia se puede extraer su texto por número (match[1]) o citarlo en el reemplazo con $1. Es una de las funciones más productivas de las expresiones regulares: por ejemplo, convertir 2026-09-09 en 09/09/2026 requiere un solo replace.
(?<year>\d{4}) es un grupo con nombre, que se lee con groups.year y se descoloca menos que un número al modificar el patrón. (?:…) es un grupo de no captura: agrupa sin ocupar número, ideal cuando solo quieres delimitar el alcance de un cuantificador o de una alternancia. \1 es una referencia inversa: exige que en ese punto vuelva a aparecer lo capturado por el grupo 1; (\w)\1 encuentra letras dobles como ll u oo, y <(\w+)>.*?</\1> encuentra etiquetas HTML emparejadas.
Nivel 18: codicia y pereza
Los cuantificadores son codiciosos por defecto: comen todo lo que pueden y, si se pasan, devuelven. Con "a" and "b", la expresión ".+" abarca de golpe desde la primera comilla hasta la última. Si se añade ? detrás del cuantificador, se vuelve perezoso: ".+?" come lo mínimo y produce dos coincidencias, "a" y "b". Otra forma más precisa es usar una clase negativa, "[^"]+", que no depende del retroceso y suele ser más rápida.
Niveles 19–20: aserciones de búsqueda (lookarounds)
Un lookaround, como un ancla, solo coincide con una posición, pero con la condición de «si al lado hay cierto patrón»:
(?=…) lookahead: detrás debe venir …; \d+(?=px) encuentra el 100 de 100px, pero no cuenta el «px» en la coincidencia.
(?!…) lookahead negativo: detrás no puede venir ….
(?<=…) lookbehind: delante debe haber …; (?<=¥)\d+ encuentra el número de un precio sin el símbolo de la moneda.
(?<!…) lookbehind negativo.
JavaScript admite lookbehind desde ES2018 y solo es totalmente utilizable desde Safari 16.4; el lookbehind de Python exige longitud fija. Validar la fortaleza de una contraseña es el uso típico: ^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$ usa tres lookaheads para comprobar desde la misma posición «tiene dígito, tiene minúscula, tiene mayúscula» y luego .{8,} consume la cadena entera.
Nivel 21: flags
Los flags se escriben fuera del patrón: i ignora mayúsculas y minúsculas; g busca todas las coincidencias y no solo la primera (el resaltado del curso usa g siempre); m hace que ^ $ coincidan en cada línea; s permite que . también coincida con el salto de línea; u activa el modo Unicode y hace que . cuente puntos de código en lugar de unidades UTF-16, con lo que un emoji es un solo carácter; y es la coincidencia pegajosa, que obliga a coincidir justo en lastIndex.
Niveles 22–24: tres patrones habituales
Móvil de China continental: ^1[3-9]\d{9}$ — empieza por 1, el segundo dígito va de 3 a 9 y son 11 en total. No intentes enumerar los prefijos de las operadoras: cada año aparecen nuevos.
Correo: una expresión totalmente conforme a RFC 5322 ocupa miles de caracteres y no resulta práctica. Una forma razonable para un formulario es ^[^\s@]+@[^\s@]+\.[^\s@]+$: exactamente una @, los dos lados sin espacios y un dominio con al menos un punto. La verificación de verdad se hace enviando un correo de confirmación.
Fecha: ^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$ limita el mes a 01–12 y el día a 01–31, pero no sabe cuántos días tiene febrero: la validación sintáctica se deja a la expresión regular y la semántica al código.
Nivel 25: backtracking catastrófico
Los motores de JavaScript, Python, Java y PCRE son de retroceso (backtracking): ante varias formas posibles de partir la cadena prueban una y, si falla, vuelven atrás y prueban la siguiente. Los cuantificadores anidados hacen que el número de formas crezca de manera exponencial: ^(a+)+$ delante de 30 a y un ! obliga al motor a probar más de 2^30 maneras de partir antes de declarar el fallo, y la pestaña del navegador se queda bloqueada de unos segundos a varios minutos. Si un patrón así acepta entrada de usuarios, es un ataque de denegación de servicio (ReDoS).
Principios para evitarlo: no dejar que dos cuantificadores contiguos puedan coincidir con los mismos caracteres ((a+)+, (\w+\s?)+, (.*)* son señales de peligro); sustituir el perezoso .*? por clases negativas; y poner un límite de longitud a la entrada. Las bibliotecas de expresiones regulares de Go y Rust usan implementaciones Thompson NFA / DFA con tiempo lineal garantizado, a cambio de no admitir referencias inversas ni lookarounds: el artículo de Russ Cox explica la diferencia entre las dos familias mejor que nadie.
Para qué no sirve una expresión regular
Emparejar estructuras anidadas (paréntesis equilibrados, un HTML completo), tareas que requieren «contar» (comprobar que hay los mismos paréntesis de apertura que de cierre) o decisiones que dependen del significado del contexto (si ese número es una fecha válida) quedan fuera de su alcance o son muy difíciles. Es una herramienta de nivel léxico: encontrar tokens, dividir, validar cosas simples y reemplazar en masa; en eso no tiene rival.