¿Qué variante de regex prueba esta herramienta?+
La que ya se ejecuta en tu navegador. Esta página no incluye ningún motor: tu patrón se entrega al propio constructor RegExp del entorno de ejecución de JavaScript, y las coincidencias que ves son las que obtendrá tu código. Eso hace que los resultados sean exactos para JavaScript, TypeScript, Node, Deno y Bun. Para cualquier otra cosa solo son aproximados. El re de Python, PCRE en PHP y Perl, RE2 de Go, el crate regex de Rust, .NET, Java, grep, sed y ripgrep difieren, a veces en la sintaxis y a veces en lo que significa la misma sintaxis. Si el patrón va destinado a otro lenguaje, pruébalo allí antes de publicarlo.
¿Cuál es la diferencia entre los cuantificadores codiciosos y los perezosos?+
Un cuantificador codicioso toma todo lo que puede y luego devuelve caracteres de uno en uno hasta que el resto del patrón encaja. Uno perezoso (que se escribe añadiendo un signo de interrogación, así: *? +? ?? {n,m}?) toma lo mínimo posible y solo crece cuando se ve obligado. La demostración clásica es /<.+>/ sobre "<a><b>": el .+ codicioso se traga todo hasta el final, retrocede hasta el último ">" y devuelve "<a><b>" como una sola coincidencia. Cámbialo a /<.+?>/ y obtienes "<a>" y luego "<b>". Ninguno es más correcto; responden a preguntas distintas. La regla práctica es que el codicioso es la opción adecuada por defecto cuando el delimitador es único, y el perezoso cuando se repite.
¿Qué es el backtracking catastrófico?+
Es lo que ocurre cuando un patrón puede dividir el mismo texto de muchas formas distintas y al final la coincidencia falla. JavaScript busca coincidencias mediante backtracking, así que, ante un fallo, reintenta todas las combinaciones restantes antes de rendirse. El desencadenante de manual es una repetición dentro de otra repetición, como /(a+)+$/ sobre una larga serie de "a" seguida de una sola "b". Hay un número exponencial de formas de dividir 30 letras "a" en grupos de una o más, y el motor las prueba todas antes de concluir que el "$" no puede coincidir. Treinta caracteres tardan milisegundos; cuarenta tardan unas mil veces más. Las soluciones son hacer que las repeticiones interior y exterior no puedan coincidir con el mismo texto, sustituir un grupo cuantificado por una clase de caracteres o anclar el patrón para que el fallo se detecte pronto. Esta página analiza tu patrón en busca de esas formas y las señala antes de ejecutarlo.
¿Por qué el lookbehind llegó tan tarde a JavaScript?+
El lookahead estaba en el lenguaje desde el principio, pero el lookbehind no llegó hasta ES2018 (más de veinte años después), y hasta que Safari lo incorporó en 2023, un patrón que lo usara lanzaba un error de sintaxis en un navegador que muchos sitios aún tenían que admitir. El retraso se debió en parte a la ralentización general de la evolución del lenguaje entre el abandono de ES4 y ES6, y en parte a que el lookbehind es realmente más difícil de implementar: el motor tiene que buscar hacia atrás desde la posición actual. La implementación de JavaScript acabó siendo mejor que la mayoría, porque, a diferencia de PCRE, el re de Python y Java, permite lookbehind de longitud variable. /(?<=\$\d+ )item/ es válido aquí y esos motores lo rechazan de plano. Si un patrón tuyo tiene que funcionar en un entorno antiguo, la solución tradicional es capturar el contexto anterior en un grupo y descartarlo después.
¿Cuál es la diferencia entre los flags u y v?+
El flag u, de ES2015, pone el patrón en modo Unicode: los caracteres astrales, como los emoji, cuentan como una unidad en lugar de dos mitades UTF-16, \u{1F600} pasa a ser válido, se habilitan los escapes de propiedad \p{…} y los escapes sin significado se convierten en errores de sintaxis en lugar de ignorarse en silencio. El flag v, de ES2024, es un superconjunto que añade notación de conjuntos dentro de las clases de caracteres (diferencia con --, intersección con && y propiedades de cadenas de varios caracteres), de modo que puedes escribir [\p{Letter}--[a-z]] para decir "cualquier letra excepto una minúscula ASCII". Los dos flags son mutuamente excluyentes: una regex está en modo u o en modo v, nunca en ambos. Esta página ofrece u y no v, porque un par de botones que se anulan entre sí sin avisar es peor interfaz que uno que es honesto sobre su alcance.
¿Por qué mi regex global se salta una coincidencia de cada dos?+
Porque una regex con el flag g o y tiene estado. Guarda una propiedad lastIndex, y tanto exec como test empiezan desde ahí y la actualizan cuando tienen éxito. Guarda una en una constante a nivel de módulo, llama a test dos veces sobre la misma cadena y la segunda llamada empieza a mitad de camino y devuelve false. El mismo error aparece cuando una regex global se reutiliza entre los elementos de un bucle. Hay tres salidas: crear la regex de nuevo donde se usa, restablecer lastIndex a 0 antes de cada llamada, o usar matchAll y String.match, que no dejan el cursor donde puedas tropezar con él. Esta página evita el problema por completo compilando una copia de trabajo privada en cada ejecución, así que la regex que estás viendo nunca es la que se modifica.
¿Qué puede hacer PCRE que las regex de JavaScript no pueden?+
Varias cosas, y saber cuáles ahorra mucha depuración confusa cuando copias un patrón de un motor a otro. No hay grupos atómicos: el (?>…) de PCRE se compromete con una coincidencia y se niega a volver atrás dentro de ella, que es el arreglo manual estándar para el backtracking catastrófico, y el único sustituto en JavaScript es un lookahead que envuelve una captura. No hay cuantificadores posesivos (a*+) por la misma razón. No hay recursión (?R) ni llamadas a subrutinas, así que los paréntesis equilibrados quedan fuera de alcance. No hay modificadores en línea: (?i) a mitad del patrón es un error de sintaxis, porque un flag de JavaScript se aplica a toda la regex o a nada. No hay condicionales, ni anclas \A \z \Z, ni clases POSIX como [[:alpha:]], ni modo de comentarios. Lo que JavaScript sí tiene y muchos motores no es el lookbehind de longitud variable.
¿\d significa lo mismo en todas partes?+
No, y la diferencia es una fuente real de errores de seguridad. En JavaScript \d es exactamente [0-9], siempre, con o sin el flag u. En .NET y Python 3, \d coincide por defecto con cualquier carácter que tenga la propiedad Unicode de dígito decimal, lo que incluye los dígitos arábigo-índicos ٠١٢, los dígitos devanagari y los de ancho completo 012. Un validador escrito en Python que acepta ٢٠٢٦ y uno en JavaScript que lo rechaza discreparán sobre la misma entrada, que es precisamente el tipo de desajuste que se explota. La misma precaución se aplica a \w, que aquí es [A-Za-z0-9_] y por tanto no considera "é" un carácter de palabra, y a \b, que se define en función de \w y por eso cae en mitad de "naïve". Si quieres semántica Unicode en JavaScript, tienes que pedirla explícitamente: \p{Nd} con el flag u para los dígitos, \p{L} para las letras.
¿Cuándo debo dejar de usar una expresión regular?+
Cuando lo que buscas puede anidarse. Las expresiones regulares en sentido formal no saben contar, y aunque las referencias inversas llevan al motor de JavaScript más allá de un lenguaje estrictamente regular, sigue sin poder reconocer estructuras equilibradas. Eso descarta HTML, XML, JSON, código fuente y cualquier lenguaje con paréntesis, no porque el patrón sea difícil de escribir sino porque no existe ningún patrón. Usa DOMParser, JSON.parse o un analizador de verdad. Tres señales más sutiles apuntan en la misma dirección: el patrón ha crecido más allá de una o dos líneas y nadie puede leerlo, necesita un comentario para explicar cada cláusula, o sigue acumulando casos especiales para entradas que se suponía que ya cubría. Una expresión regular rinde al máximo cuando busca y valida fragmentos planos y bien formados (una fecha, un color hexadecimal, un prefijo de log), y rinde al mínimo cuando finge ser una gramática.
¿Qué significan $1, $&, $` y $' en un reemplazo?+
Son los patrones de sustitución que String.replace entiende en la cadena de reemplazo. De $1 a $99 insertan el texto de ese grupo de captura numerado, y $<name> hace lo mismo con uno con nombre. $& inserta la coincidencia completa. $` inserta todo lo que hay antes de la coincidencia y $' todo lo que hay después; ambos son sorprendentemente útiles para envolver texto y sorprendentemente fáciles de activar por accidente, ya que una barra invertida no los escapa. $$ inserta un único signo de dólar literal. Dos reglas sorprenden a mucha gente: una referencia a un grupo que no existe se deja en la salida como texto plano en lugar de lanzar un error, y $12 significa el grupo 12 si el patrón tiene doce grupos y el grupo 1 seguido del carácter "2" si no los tiene. Esta página los expande todos en la vista previa y señala cualquier referencia que no haya podido resolver.