design
Regex Tester & Pattern Builder: Una guía práctica para crear patrones que sí funcionan
Cómo construir expresiones regulares que realmente funcionan: anclas, cuantificadores, las trampas entre motores y el patrón ReDoS que puede colgar tu servidor. Prueba mientras avanzas.
Published 2026-06-22 · 9 min read
Affiliate disclosure
Some links below are affiliate links. I may earn a commission from qualifying purchases at no extra cost to you. I only recommend tools I have used or tested.

TL;DR
- Prueba antes de enviar a producción: un regex tester detecta errores que adivinar no puede. Los patrones fallan de maneras que no puedes predecir sin ejecutarlos.
- Los motores divergen:
\dcoincide con más de 600 dígitos Unicode en Python, pero solo con ASCII 0–9 en PCRE a menos que lo actives. La sintaxis y el soporte del lookbehind varían. Prueba tu regex en el motor que realmente vas a usar.- Cuidado con el backtracking catastrófico: patrones como
^(a+)+$pueden colgarse o agotar el tiempo de espera cuando la entrada no coincide, porque el motor explora exponencialmente muchos caminos. Usa un motor de tiempo lineal como RE2 para entradas no confiables.
¿Por qué es mejor usar un regex tester en lugar de adivinar?
La regex es de solo lectura hasta que la pruebas. Un patrón que parece a prueba de balas en tu cabeza suele fallar cuando se encuentra con cadenas reales: coincide con demasiado, con muy poco, o el motor se queda colgado. El enfoque de "primero probar" detecta estos fallos antes de que lleguen a producción. Un regex tester te permite pegar un patrón, darle cadenas de prueba y ver exactamente qué coincide y qué grupos de captura extrae. El de anytools ejecuta el motor de JavaScript completamente en tu navegador (nada sale de la página), muestra los grupos de captura nombrados y numerados, te permite alternar flags como g, i, m y s, y tiene un modo de reemplazo para probar sustituciones. Tú observas el comportamiento en lugar de imaginarlo.
La otra ventaja es la velocidad. En lugar de escribir código, ejecutar una suite de pruebas y esperar el resultado, obtienes retroalimentación instantánea en el navegador. Editas un patrón, ves cómo se reevalúa e iteras en segundos. Para cuando portas la regex a tu código de producción, ya sabes que funciona en las entradas que te importan.
Los bloques de construcción — qué hace realmente un patrón
Antes de poder escribir una regex que funcione, necesitas entender qué hace cada pieza y en dónde te pueden engañar.
Los anclajes fijan tu coincidencia a los bordes. ^ coincide con el inicio de la cadena (o línea, si el flag multilínea está activo) y $ coincide con el final. Sin ellos, 123 coincidirá dentro de "prefijo123sufijo". Con ellos, ^123$ coincide solo con la cadena "123" exactamente. Un error clásico: olvidar los anclajes y preguntarse por qué el patrón coincide con cosas que no debería. El truco aquí es que el significado de ^ y $ cambia con el flag multilínea: JavaScript requiere el flag /m para hacer que coincidan con los límites de línea en lugar de los límites de la cadena, según MDN.
Las clases de caracteres definen qué puede coincidir. [a-z] significa "una letra minúscula". [0-9] significa un dígito. \d es un atajo para los dígitos, pero aquí está la trampa: en Python 3, \d coincide con cualquier dígito decimal Unicode, incluidos arábigos (٠١٢) y Devanagari (०१२), no solo 0–9. En PCRE, el valor predeterminado es solo ASCII [0-9] a menos que se establezca el flag PCRE2_UCP según la documentación de Python. Si tu código espera que \d signifique 0–9 pero lo llevas a Python, en silencio empezarás a coincidir con caracteres que nunca quisiste.
Los cuantificadores indican cuántas veces repetir. + significa una o más veces. * significa cero o más. ? significa cero o una. {2,5} significa entre 2 y 5 veces. Por defecto, estos son codiciosos (greedy): coinciden con la mayor cantidad de caracteres posible. El patrón <div>.*</div> coincidirá desde el primer <div> hasta el último </div> en la cadena, tragándose todo lo que haya en medio. Rexegg documenta que agregar ? hace que un cuantificador sea flojo (lazy): <div>.*?</div> se detiene en el primer </div>. Para casos simples, los cuantificadores flojos parecen la solución, pero no son una bala de plata. Un cuantificador flojo aún retrocede (backtrack), y si falta la etiqueta de cierre por completo, el motor seguirá explorando múltiples caminos antes de rendirse, lo que puede desencadenar la trampa ReDoS que veremos más abajo.
Los grupos de captura extraen partes de la coincidencia. Envolver un patrón entre paréntesis, como (\d+)-(\w+), crea dos grupos de captura. Después de una coincidencia, puedes obtener la coincidencia completa y cada grupo por separado. Los grupos de captura nombrados, como (?<day>\d+)-(?<month>\w+), etiquetan los grupos para que no tengas que contarlos: código más claro, menos errores de "fuera por uno" (off-by-one). JavaScript soporta grupos nombrados y lookbehind (con ancho fijo), Python soporta ambos, PCRE soporta ambos, pero POSIX ERE no según la especificación POSIX. La advertencia: tu regex tiene que ejecutarse en un motor que tenga estas características.
El problema de los motores — tu regex funciona aquí, se rompe allá
Los motores de regex son como dialectos de un mismo idioma base, y divergen en formas que crean errores silenciosos.
POSIX ERE es la línea base. Soporta los cuantificadores +, *, ?, {m,n}, agrupamiento y alternancia. No tiene lookahead, lookbehind, backreferences ni grupos nombrados; esas son extensiones de PCRE/Perl/Python/JavaScript. Si estás escribiendo una regex para una herramienta del sistema como grep o sed, o para una librería de validación simple, POSIX es lo que estás usando.
El módulo re de Python coincide con Unicode por defecto. Como mencionamos antes, \d es la clase decimal Unicode en Python 3. \w coincide con cualquier carácter de palabra Unicode, no solo ASCII. Esto es útil para la internacionalización, pero rompe tus suposiciones si estás portando código desde el mundo de PCRE, donde \d significa ASCII 0–9.
PCRE (y Perl) ofrecen la mayor cantidad de funciones. Lookbehind, backreferences, grupos nombrados, grupos atómicos, cuantificadores posesivos... si parece un superpoder de regex, PCRE probablemente lo tenga. El costo está en la complejidad y la portabilidad. Un patrón PCRE no funciona en JavaScript o Python sin ajustes.
JavaScript tiene la mayoría de las funciones modernas, pero lookbehind de ancho fijo. Grupos nombrados, lookahead, lookbehind (dependiendo): todo soportado. El problema: una aserción lookbehind como (?<=user_) debe tener un patrón de ancho fijo, lo que significa que no puedes usar + o * dentro de ella. (?<=[a-z]+) arrojará un error de sintaxis en JavaScript según MDN. PCRE permite lookbehind de ancho variable, lo cual es más potente pero más lento.
RE2 y el paquete regexp de Go priorizan la velocidad sobre las funciones. Garantizan una coincidencia en tiempo lineal O(mn) (donde m es el tamaño del patrón y n es la entrada), haciéndolos inmunes al backtracking catastrófico. A cambio: nada de lookbehind, ni backreferences, ni cuantificadores posesivos. Si te importa más la seguridad y la velocidad que tener todas las funciones, RE2 es la opción correcta. Google lo construyó para manejar de forma segura patrones suministrados por usuarios.
La trampa ReDoS — por qué ^(a+)+$ puede colgar tu servidor
Esta es la amenaza de regex más subestimada. Un patrón puede ser sintácticamente válido y semánticamente sin sentido, pero aún así hacer que el motor de regex se cuelgue o supere el tiempo de espera cuando intenta coincidir con ciertas entradas. Esto se llama backtracking catastrófico o ReDoS (Denegación de Servicio por Expresiones Regulares), y ocurre cuando los cuantificadores anidados obligan al motor a explorar exponencialmente muchos caminos.
El ejemplo clásico es ^(a+)+$. Dale la cadena aaaaaaaaaaaaaaa! (quince a seguidas de un !). El motor intenta coincidir con el patrón. El + exterior es codicioso, por lo que consume las quince a. El + interior también es codicioso, así que quiere más. El motor retrocede, le da menos caracteres al + interior e intenta de nuevo. Sigue probando diferentes divisiones de las a entre los dos cuantificadores. Para solo 16 a seguidas de un fallo de coincidencia, el motor explora 65,536 posibles caminos de retroceso; exponencial. Si duplicas la longitud de la entrada, el número de caminos explota.
Si usas un regex tester para darle a este patrón una cadena de prueba de 20+ a seguidas de un carácter que no coincide, verás el cuelgue o el tiempo de espera agotado en tiempo real. La solución es evitar los cuantificadores anidados. ^a+$ hace el mismo trabajo sin la trampa. Para patrones más complejos, la verdadera solución es usar un motor como RE2 que rechaza el backtracking por completo y garantiza un tiempo lineal.
La trampa: los cuantificadores anidados a menudo son accidentales. Un patrón como (col|column)umn? parece inofensivo, pero si un usuario suministra una entrada que no coincide, el motor puede atascarse intentando diferentes formas de dividir la entrada entre las dos alternativas similares a cuantificadores. Es por eso que aceptar patrones regex de usuarios no confiables es peligroso. Siempre valida con RE2 o restringe el patrón a una lista blanca.
Para los patrones que tú mismo escribes y pruebas, el riesgo es menor, pero sigue siendo real si aceptas patrones regex de fuentes externas (un archivo de configuración, un parámetro de API, entrada del usuario). Pruébalos en un regex tester con casos extremos primero.
La trampa clásica — la validación de correos electrónicos es más difícil de lo que parece
Validar un correo electrónico suena simple: ^[a-zA-Z0-9.+]+@[a-zA-Z0-9]+\.[a-z]{2,}$. No lo es. El RFC 5322 (la especificación de correos) permite =, _ y otros caracteres en la parte local que la mayoría de las regexes no contemplan. Más sutilmente, un cuantificador codicioso como .+ coincidirá de más. En usuario.correo@example.com, el patrón ^[a-zA-Z0-9.+]+@ consumirá todos los caracteres hasta el último @ si hay varios, lo cual es incorrecto. La regex "correcta" que maneja todos los casos del RFC 5322 es tan larga y enrevesada que los expertos coinciden en que no vale la pena escribir.
La solución práctica: no valides correos electrónicos con regex. Acepta la entrada, envía un correo de confirmación a esa dirección y verifica que el usuario lo haya recibido. Esa es la única validación real. Si quieres una verificación rápida con regex para detectar errores obvios —falta un @, falta el dominio— usa algo permisivo como ^[^\s@]+@[^\s@]+$ (cualquier cosa excepto espacios en blanco o @ en ambos lados) y deja que el paso de confirmación maneje el resto. Prueba algunos correos electrónicos del mundo real (incluyendo nombre.apellido+etiqueta@example.co.uk) en un regex tester antes de decidir que un patrón es lo suficientemente bueno.
Cómo difieren realmente los motores de regex — una matriz rápida
| Motor / Variante | Lookbehind | Grupos nombrados | Backreferences | Unicode \p{} | Tiempo lineal (seguro contra ReDoS) |
|---|---|---|---|---|---|
| POSIX ERE | ✗ | ✗ | ✗ | ✗ | ✓ (simple) |
Python 3 re | ✓ (ancho fijo) | ✓ ((?P<name>)) | ✓ | Parcial (\w/\d compatibles con Unicode; \p{} solo en el módulo regex) | ✗ |
| PCRE | ✓ | ✓ (?<name>) | ✓ | ✓ | ✗ |
| JavaScript | ✓ (solo ancho fijo) | ✓ (?<name>) | ✓ | ✓ (con el flag u) | ✗ |
| RE2 / Go | ✗ | ✗ | ✗ | Limitado | ✓ (garantizado O(mn)) |
La conclusión clave: más funciones no significan más seguridad o rapidez. RE2 está limitado intencionalmente para evitar ReDoS y garantizar un tiempo lineal. Python y JavaScript ofrecen flexibilidad para patrones complejos. POSIX es el mínimo común denominador. Elige el motor que se adapte a tus limitantes: si la velocidad y la seguridad importan más que las características, elige RE2 o el regexp de Go. Si necesitas las funciones, asume el riesgo de backtracking y valida los patrones en un probador primero.
Patrones comunes — y por qué cada uno es incompleto
Una rápida hoja de ayuda con los patrones que vas a usar, con la sincera advertencia de que cada uno es imperfecto y asume tu caso de uso específico.
Número de teléfono (formato EE. UU. aprox.): ^\d{3}-\d{3}-\d{4}$ coincide con 555-123-4567. Pero no maneja extensiones, códigos de país ni espacios. Y recuerda: \d es Unicode en Python, ASCII en PCRE. Es mejor analizar y validar por separado.
Fecha ISO (AAAA-MM-DD): ^\d{4}-\d{2}-\d{2}$ coincide con el formato, pero no verifica si la fecha es válida (¿existe el 30 de febrero?). Valida el formato con regex y luego analiza y valida la lógica en tu código.
URL (simplificada): ^https?://[^\s]+$ permite cualquier carácter después del dominio, excepto espacios en blanco. Las URL reales son más desordenadas (parámetros de consulta, fragmentos, codificaciones). Usa una librería para analizar URL si necesitas precisión.
Línea CSV: ^[^,]*,[^,]*,[^,]*$ coincide con tres campos separados por comas sin ningún manejo especial para campos entre comillas o comas escapadas. Una regex nunca manejará el CSV correctamente; usa un analizador de CSV.
Cada patrón anterior funciona para su caso específico, pero falla en las variantes. Siempre pasa cadenas de prueba reales (casos extremos, caracteres internacionales, formatos extraños) por un regex tester antes de confirmarlo.
Herramientas complementarias una vez que tengas un patrón. Después de crear un patrón regex, a menudo necesitas manipular las cadenas coincidentes. Un convertidor de mayúsculas y minúsculas se encarga de pasar la salida coincidente al formato que necesites. Para depurar o registrar los resultados de la regex, un formateador JSON embellece los objetos coincidentes para que puedas ver los grupos de captura con claridad. Y si tu regex está destinado a validar componentes de una URL, un codificador de URL prueba si tu patrón funciona bien con caracteres seguros para URL.
Entonces, ¿qué motor deberías elegir realmente?
Tres reglas rápidas cubren la mayoría de los casos. ¿Patrones de coincidencia de usuarios no confiables (un cuadro de búsqueda, un campo de API, un archivo de configuración que alguien más edita)? Usa RE2 o el regexp de Go: la garantía de tiempo lineal vale la pérdida del lookbehind. ¿Escribir un patrón para tu propio código en Python, JavaScript o alguna herramienta PCRE? Usa el motor que tu lenguaje ya trae; solo recuerda las diferencias en el atajo de dígitos y de lookbehind cuando copias un patrón entre ellos. ¿Haciendo una coincidencia única en una herramienta de shell como grep o sed? Estás en POSIX, así que omite por completo el lookaround y las backreferences.
Sea cual sea el que elijas, construye el patrón de forma incremental y míralo ejecutarse antes de que toque producción.
La conclusión final
Conclusión: Construye los patrones paso a paso en el regex tester de anytools, pruébalos en el motor donde realmente vas a desplegar y aprende las trampas de ese motor. Evita los cuantificadores anidados, prefiere la negación (
[^<]) sobre los cuantificadores flojos y nunca valides un correo electrónico con una sola regex: acepta la entrada, confírmala por separado y deja que el código revise la lógica. Para patrones no confiables, usa RE2; para los tuyos, pégalos en el tester con algunos casos extremos feos antes de confirmarlos.





