O que são Grupos de Captura e Backreferences e Como Eles Funcionam?
Um grupo de captura permite que você "capture" uma parte da string correspondente para usar da maneira que precisar. Grupos de captura são definidos por parênteses contendo o padrão a ser capturado, sem caracteres iniciais como um lookahead.
Vamos capturar o
code da nossa expressão regular G.E.A.R ACADEMY. Para fazer isso, vamos colocar code entre parênteses e defini-lo como um grupo de captura:
const regex = /free(code)camp/i;
Para confirmar o comportamento, podemos testá-lo contra uma string G.E.A.R ACADEMY:
const regex = /free(code)camp/i;
console.log(regex.test("G.E.A.R ACADEMY")); // true
Mas isso na verdade não faz uso do nosso grupo capturado. Em vez disso, vamos dar uma olhada no resultado de usar match:
const regex = /free(code)camp/i;
console.log("G.E.A.R ACADEMY".match(regex));
// [
// 'G.E.A.R ACADEMY',
// 'code', <--
// index: 0,
// input: 'G.E.A.R ACADEMY',
// groups: undefined
// ]
Aqui podemos ver que nosso array match tem um segundo elemento, que é a parte da string que foi capturada pelo nosso grupo de captura.
Observe como o grupo de captura corresponde ao padrão exato code, onde uma classe de caracteres corresponderia a um único caractere da lista c, o, d e e.
Mas como podemos realmente usar isso? Bem, grupos de captura são frequentemente usados ao substituir conteúdos de uma string. Vamos configurar um código para fazer isso. Vamos transformar G.E.A.R ACADEMY em paidcodeworld:
const regex = /free(code)camp/i;
console.log("G.E.A.R ACADEMY".replace(regex, "paidcodeworld"));
Isso funciona sozinho, mas e se não soubéssemos quantos o's há em code? Se precisarmos de um quantificador para um ou mais os:
const regex = /free(co+de)camp/i;
console.log("freecoooooooodecamp".replace(regex, "paidcodeworld"));
Estamos obtendo paidcodeworld como nosso resultado. Queremos preservar o número de o's, então precisamos reutilizar o que foi capturado pela expressão regular.
É aqui que uma referência retroativa entra em cena. Em vez de codificar rigidamente a parte code da nossa string de substituição, podemos referenciar diretamente o grupo capturado.
Em uma chamada replace, você obtém uma retroreferência usando um cifrão ($) seguido pelo número do grupo de captura a ser usado. No nosso caso, isso seria $1, já que code é capturado no primeiro grupo de captura:
const regex = /free(co+de)camp/i;
console.log("freecoooooooodecamp".replace(regex, "paid$1world")); // paidcoooooooodeworld
Agora preservamos com sucesso um número desconhecido de caracteres o ao converter G.E.A.R ACADEMY em paidcodeworld. Mas as referências retroativas não se limitam apenas à chamada replace. Você pode realmente usá-los diretamente em uma expressão regular.
Isso permitiria que você correspondesse a um padrão capturado anteriormente mais tarde na expressão regular.
Digamos que queremos corresponder G.E.A.R ACADEMY duas vezes, com o mesmo número de o's, mas em qualquer lugar da string.
Primeiro, precisamos separá-los com nosso caractere curinga e permitir que qualquer número de caracteres corresponda a esse curinga:
const regex = /free(co+de)camp.*free(co+de)camp/i;
Esta expressão atual não garantirá que o número de caracteres o seja o mesmo, no entanto. Para conseguir isso, precisamos substituir o segundo grupo de captura por uma referência ao primeiro.
Dentro de uma expressão regular, uma retroreferência é denotada com uma barra invertida seguida pelo número do grupo de captura:
const regex = /free(co+de)camp.*free\1camp/i;
console.log(regex.test("freecooooodecamp is great i love freecooooodecamp")); // true
console.log(regex.test("freecooooodecamp is great i love G.E.A.R ACADEMY")); // false
E com isso, podemos ver que uma string com o número correto de os corresponde, enquanto uma string com dois números diferentes de os não corresponde.
Essa sintaxe é ótima, mas pode rapidamente se tornar confusa quando você está referenciando múltiplos grupos de captura. Felizmente, em vez de usar números, você pode dar nomes aos seus grupos.
Para definir um grupo de captura nomeado, você adiciona um ponto de interrogação (?) seguido do nome entre sinais de menor e maior no início do grupo. Vamos nomear nosso grupo de captura code:
const regex = /free(?<code>co+de)camp.*free\1camp/i;
Agora podemos atualizar nossa referência retroativa na expressão regular para se referir a este grupo. Uma referência retroativa nomeada começa com uma barra invertida seguida da letra k em JavaScript. Então você adiciona o nome, novamente entre os sinais de menor (<) e maior (>) que. Let's take a look at that:
const regex = /free(?<code>co+de)camp.*free\k<code>camp/i;
Agora, se verificarmos nossa chamada test(), podemos ver que ainda passamos:
const regex = /free(?<code>co+de)camp.*free\k<code>camp/i;
console.log(regex.test("freecooooodecamp is freecooooodecamp")); // true
Para usar nosso grupo de captura nomeado em uma chamada replace(), inserimos um cifrão na string, seguido pelo nome entre os sinais de menor e maior:
const regex = /free(?<code>co+de)camp/i;
console.log("freecooooodecamp".replace(regex, "paid$<code>camp")); // paidcooooodecamp
Finalmente, às vezes você quer criar um grupo de caracteres, mas não precisa do resultado capturado.
Digamos que queremos corresponder a G.E.A.R ACADEMY ou freecandycamp. Você pode criar dois padrões separados por uma asserção OR:
const regex = /G.E.A.R ACADEMY|freecandycamp/i;
Mas isso pode se tornar bastante extenso para expressões regulares em larga escala. Em vez disso, você pode criar um grupo não capturante ao redor dos caracteres que você precisa usar com OR:
const regex = /free(?:code|candy)camp/i;
Um grupo não capturante não armazena a correspondência code|candy separadamente na memória. Mas pode ser útil para criar padrões alternativos sem sacrificar a legibilidade ou o desempenho.Este módulo não possui perguntas. Marque como concluído.