Referencias inversas en regex: \n y \k<name>
Aprende qué son las referencias inversas en expresiones regulares de JavaScript, cómo usar \1, \k<name> y $1 en replace().
La mayor parte de una expresión regular coincide con texto fijo, pero a veces necesitas coincidir con texto que debe ser idéntico a algo que ya capturaste antes — sin saber de antemano cuál es ese texto. Una referencia inversa resuelve exactamente esto. Permite que un patrón diga "coincide con lo mismo que el grupo capturó hace un momento".
Los usos clásicos de las referencias inversas incluyen detectar una palabra duplicada (the the), hacer coincidir una cadena envuelta en comillas balanceadas ("..." o '...' pero no "...'), o verificar que una etiqueta similar a HTML se cierra con el mismo nombre de etiqueta. Ninguno de estos casos es posible con patrones literales ordinarios, porque el texto a coincidir no se conoce hasta que se ejecuta la expresión regular.
Esta guía cubre las referencias inversas numeradas (\1, \2, …), las referencias inversas con nombre (\k<name>), cómo se numeran los grupos, los errores comunes y cómo reutilizar el texto capturado en String.prototype.replace().
Cómo usar las referencias inversas
Dentro de un patrón, una barra invertida seguida de un número hace referencia al texto capturado por un grupo de captura. \1 es lo que coincidió el grupo 1, \2 es el grupo 2, y así sucesivamente. El punto clave: coincide con el texto capturado, no con el patrón del grupo de nuevo.
Aquí (\w+) captura una palabra en el grupo 1, \s coincide con el espacio, y \1 requiere la misma palabra de nuevo. Por tanto, hello hello coincide, pero hello world no — \1 debe ser igual a lo que capturó el grupo 1, no simplemente coincidir con \w+ una segunda vez.
Cómo se numeran los grupos
Los números de grupo se asignan por la posición del paréntesis de apertura de cada grupo, de izquierda a derecha. Esto importa cuando tienes múltiples grupos o grupos anidados:
La coincidencia completa es el grupo 0 (m[0]), por eso el primer grupo de captura es \1, no \0. En los grupos anidados, el grupo exterior recibe el número más bajo porque su ( aparece primero.
Uso de grupos con nombre
Las referencias numeradas se vuelven difíciles de leer a medida que el patrón crece. En su lugar, puedes nombrar un grupo con (?<name>…) y referenciarlo con \k<name>. Para más información sobre cómo declarar grupos con nombre, consulta Grupos de captura.
Aquí (?<word>\w+) es un grupo con nombre y \k<word> lo referencia de forma inversa. Tras una coincidencia exitosa, el texto capturado también está disponible en el objeto match.groups. Los grupos con nombre y \k<name> funcionan en todos los navegadores modernos y en las versiones actuales de Node.js sin ninguna bandera.
Reutilización de capturas en replace()
El uso más común en el día a día de las referencias inversas no es dentro del patrón — sino en la cadena de reemplazo de String.prototype.replace(). Allí se referencia el texto capturado con $1, $2, … (o $<name> para grupos con nombre).
Un ejemplo útil colapsa una palabra duplicada accidentalmente en una sola:
Ten en cuenta la distinción: \1 (barra invertida) se usa dentro del patrón, mientras que $1 (signo de dólar) se usa en la cadena de reemplazo. Confundirlos es una fuente frecuente de errores.
Error frecuente: grupos que no participan
Una referencia inversa a un grupo que no participó en la coincidencia tiene un comportamiento especial. Si el grupo nunca coincidió (por ejemplo, estaba dentro de una alternativa no usada), su valor capturado es undefined, y en JavaScript la referencia inversa entonces coincide con la cadena vacía — tiene éxito sin consumir nada.
Esto es fácil de pasar por alto: podrías esperar que \1 falle cuando el grupo no coincidió, pero en cambio coincide silenciosamente con nada. Estructura cuidadosamente tu alternancia si dependes de que un grupo siempre haya capturado algo.
Una referencia inversa auténtica: comillas balanceadas
Un patrón práctico que requiere una referencia inversa es hacer coincidir una cadena entre comillas donde el carácter de cierre debe ser el mismo que el de apertura — "..." y '...' son válidos, pero "...' no lo es.
El grupo (['"]) captura el carácter de comilla que abrió la cadena, y \1 obliga a que el cierre sea ese mismo carácter. Un simple ["'].*?["'] no podría garantizar esto — coincidiría alegremente con "...'. Esta es la diferencia entre un lookahead/lookbehind (que solo afirma) y una referencia inversa (que vuelve a coincidir con el texto capturado).
Conclusión
Recurre a una referencia inversa cuando una parte posterior de la coincidencia debe ser igual al texto coincidido antes — palabras duplicadas, comillas balanceadas, etiquetas con el mismo nombre, o reglas del tipo "los caracteres adyacentes deben diferir". Recuerda los tres puntos esenciales:
- Los grupos numerados se cuentan por su
(de apertura, comenzando en\1; la coincidencia completa es el grupo 0. - Usa
\1/\k<name>dentro del patrón, y$1/$<name>enreplace(). - Un grupo que no participa hace que su referencia inversa coincida con la cadena vacía, así que protege bien tus alternancias.
Para los bloques de construcción, revisa Grupos de captura, Clases de caracteres y Lookahead y Lookbehind.