W3docs

Referências retrospectivas em regex: \n e \k<name>

Entenda as referências retrospectivas em expressões regulares no JavaScript: como usar \1, \k<name> e $1 em replace() com exemplos práticos.

A maior parte de uma expressão regular corresponde a texto fixo, mas às vezes você precisa corresponder a um texto que deve ser idêntico a algo que você capturou antes — sem saber antecipadamente qual é esse texto. Uma referência retrospectiva resolve exatamente isso. Ela permite que um padrão diga "corresponda à mesma coisa que o grupo capturou há pouco."

Usos clássicos para referências retrospectivas incluem detectar uma palavra duplicada (the the), corresponder a uma string envolvida em aspas balanceadas ("..." ou '...', mas não "...'), ou verificar se uma tag HTML-like é fechada pelo mesmo nome de tag. Nada disso é possível com padrões literais comuns, pois o texto a corresponder não é conhecido até que a regex seja executada.

Este guia aborda referências retrospectivas numeradas (\1, \2, …), referências retrospectivas nomeadas (\k<name>), como os grupos são numerados, as armadilhas mais comuns e como reutilizar texto capturado em String.prototype.replace().

Como usar referências retrospectivas

Dentro de um padrão, uma barra invertida seguida de um número refere-se ao texto capturado por um grupo de captura. \1 é qualquer coisa que o grupo 1 correspondeu, \2 é o grupo 2, e assim por diante. O ponto-chave: ele corresponde ao texto capturado, não ao padrão do grupo novamente.

javascript— editable

Aqui (\w+) captura uma palavra no grupo 1, \s corresponde ao espaço, e \1 exige a mesma palavra novamente. Então hello hello corresponde, mas hello world não — \1 deve ser igual ao que o grupo 1 capturou, não apenas corresponder a \w+ uma segunda vez.

Como os grupos são numerados

Os números dos grupos são atribuídos pela posição do parêntese de abertura de cada grupo, da esquerda para a direita. Isso importa quando você tem múltiplos grupos ou grupos aninhados:

javascript— editable

A correspondência inteira é o grupo 0 (m[0]), por isso o primeiro grupo de captura é \1, não \0. Para grupos aninhados, o grupo externo recebe o número menor porque seu ( vem primeiro.

Usando grupos nomeados

As referências numeradas ficam difíceis de ler conforme o padrão cresce. Em vez disso, você pode nomear um grupo com (?<name>…) e referenciá-lo com \k<name>. Para mais detalhes sobre como declarar grupos nomeados, consulte Grupos de Captura.

javascript— editable

Aqui (?<word>\w+) é um grupo nomeado e \k<word> o referencia retrospectivamente. Após uma correspondência bem-sucedida, o texto capturado também está disponível no objeto match.groups. Grupos nomeados e \k<name> funcionam em todos os navegadores modernos e no Node.js atual sem nenhuma flag.

Reutilizando capturas em replace()

O uso mais comum do dia a dia de referências retrospectivas não é dentro do padrão — é na string de substituição de String.prototype.replace(). Lá você referencia o texto capturado com $1, $2, … (ou $<name> para grupos nomeados).

Um exemplo elegante colapsa uma palavra duplicada acidentalmente em uma só:

javascript— editable

Observe a distinção: \1 (barra invertida) é usado dentro do padrão, enquanto $1 (cifrão) é usado na string de substituição. Confundi-los é uma fonte frequente de bugs.

Armadilha: grupos não participantes

Uma referência retrospectiva a um grupo que não participou da correspondência tem um comportamento especial. Se o grupo nunca correspondeu (por exemplo, estava dentro de uma alternativa não utilizada), seu valor capturado é undefined, e em JavaScript a referência retrospectiva então corresponde à string vazia — ela tem sucesso sem consumir nada.

javascript— editable

Isso é fácil de deixar passar: você pode esperar que \1 falhe quando o grupo não correspondeu, mas em vez disso ele silenciosamente corresponde a nada. Estruture sua alternância com cuidado se você depende de um grupo ter sempre capturado algo.

Uma referência retrospectiva genuína: aspas balanceadas

Um padrão prático que requer uma referência retrospectiva é corresponder a uma string entre aspas onde a aspa de fechamento deve ser o mesmo caractere que a de abertura — "..." e '...' são válidos, mas "...' não é.

javascript— editable

O grupo (['"]) captura qualquer caractere de aspa que abriu a string, e \1 força o fechamento a ser exatamente esse caractere. Um simples ["'].*?["'] não poderia garantir isso — ele corresponderia felizmente a "...'. Esta é a diferença entre um lookahead/lookbehind (que apenas afirma) e uma referência retrospectiva (que corresponde ao texto capturado novamente).

Conclusão

Use uma referência retrospectiva sempre que uma parte posterior da correspondência precisar ser igual ao texto correspondido anteriormente — palavras duplicadas, aspas balanceadas, tags com o mesmo nome, ou regras do tipo "caracteres adjacentes devem ser diferentes". Lembre-se dos três pontos essenciais:

  • Grupos numerados são contados pelo seu ( de abertura, começando em \1; a correspondência inteira é o grupo 0.
  • Use \1 / \k<name> dentro do padrão, e $1 / $<name> em replace().
  • Um grupo não participante faz sua referência retrospectiva corresponder à string vazia, portanto proteja suas alternâncias.

Para os blocos de construção, revise Grupos de Captura, Classes de Caracteres e Lookahead e Lookbehind.

Prática

Prática
Qual das afirmações a seguir sobre referências retrospectivas em expressões regulares JavaScript é verdadeira?
Qual das afirmações a seguir sobre referências retrospectivas em expressões regulares JavaScript é verdadeira?
Was this page helpful?