La mayoría de los fallos de expresiones regulares no son errores de sintaxis. Un patrón puede ser perfectamente válido y seguir estando mal: casa demasiado, casa demasiado poco o — peor aún — bloquea la página cuando se encuentra con una cadena hostil. Entre un patrón que funciona con los ejemplos de su cabeza y uno que funciona con datos reales está la prueba.
Este artículo describe un flujo de trabajo práctico para escribir, probar y depurar expresiones regulares en el navegador, más los fallos que más muerden.
Prueba con datos reales, no con juguetes
Las cadenas de juguete adulan a un patrón. La entrada real trae saltos de línea que olvidó, espacios finales, emoji, valores vacíos y cuarenta kilobytes de marcado copiado.
- Pegue la peor muestra real que tenga — idealmente la que ya rompió algo.
- Incluya la cadena vacía, un solo carácter y una línea muy larga.
- Incluya texto no ASCII si la entrada puede contenerlo:
Ü,日本語,🙂. - Cuente los aciertos, no solo el primero. Los cuantificadores con desfase de uno y el solapamiento codicioso solo aparecen aquí.
Probar con resaltado en vivo lo abarata: cada posición de acierto se actualiza mientras edita el patrón, en lugar de aparecer tres días después en un archivo de registro.
Los flags cambian el significado de un patrón
La misma expresión se comporta de forma distinta según los flags con los que se ejecute:
| Flag | Efecto | Sorpresa típica |
|---|---|---|
i |
ignora mayúsculas y minúsculas | también casa identificadores que quería distinguir |
g |
global — encuentra todos los aciertos | en JavaScript, test() y exec() conservan estado entre llamadas |
m |
multilínea — ^ y $ casan por línea |
^ ya no significa «inicio de la cadena» |
s |
el punto también casa saltos de línea | .* empieza a cruzar líneas |
u |
modo Unicode | imprescindible para escapes por punto de código |
El flag g de JavaScript merece mención aparte: con él, regex.test() incrementa lastIndex, de modo que llamadas repetidas al mismo objeto devuelven respuestas distintas. Es una fuente clásica del fallo «funciona una vez y falla al reintentar».
Grupos de captura y grupos con nombre
Los grupos extraen datos, pero también cuestan memoria y vuelven el patrón más difícil de leer.
// los grupos con nombre sobreviven al reordenar; los numerados, no
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const m = "2026-09-22".match(re);
m.groups.year; // "2026"
m.groups.month; // "09"
Prefiera los grupos con nombre a los numerados, para que reordenar el patrón no desplace sus referencias en silencio. Si necesita paréntesis solo para agrupar y no para capturar, use un grupo sin captura: (?:...).
Backtracking catastrófico
Cuantificadores anidados como (a+)+$ o (\s*)* pueden llevar a un motor a tiempo exponencial con cadenas que casi coinciden. El patrón es instantáneo con entradas amigables y se cuelga con una hostil — y a una hostil es exactamente a la que envía un atacante.
- Evite cuantificadores sobre grupos que ya contienen cuantificadores.
- Ancle cuando pueda.
^y$permiten descartar pronto. - Someta el patrón a una cadena larga que casi no coincide. Si el tiempo de coincidencia sube mucho al añadir caracteres, el patrón no es seguro.
- Considere un motor de tiempo lineal (estilo RE2) para entradas no confiables en un servidor.
Mantenga los patrones legibles
- Nombre el patrón en el código —
const ISO_DATE = /.../gana a un literal anónimo repetido en seis archivos. - Use el modo de espaciado libre (
x) si el motor lo admite, para añadir comentarios y espacios dentro del patrón. - Sea explícito con los dígitos.
[0-9]significa dígitos ASCII;\dtambién casa otros sistemas de escritura en modo Unicode. - Deje dos ejemplos con el patrón: uno que debe coincidir y otro que no debe.
Un flujo de trabajo práctico
| Paso | Qué comprueba |
|---|---|
| 1. Escriba el patrón más pequeño que acierte una muestra real | el resaltado cae donde espera |
| 2. Añada muestras negativas | sin aciertos en entradas que deben fallar |
| 3. Active los flags deliberadamente | cada flag está ahí por un motivo |
| 4. Someta el patrón a entradas largas y hostiles | el acierto aparece al instante |
| 5. Pase a código con grupos con nombre | sin referencias posicionales sueltas |
Pruébalo
Abra un probador de regex, pegue su patrón y una muestra real, y observe cómo se resalta cada acierto mientras escribe. Como la prueba se ejecuta en su navegador, el texto que está validando nunca sale de su equipo — y eso importa en cuanto la muestra contiene un token, una clave o datos de clientes.