Menu

Regex em JavaScript: test, match, replace e grupos

Como as expressões regulares funcionam em JavaScript: como criar padrões, os métodos principais (test, match, replace), as flags e os grupos de captura com exemplos práticos.

Uma Regex é um Padrão para Encontrar Texto

Expressões regulares descrevem formatos de strings: "quatro dígitos", "uma palavra seguida de vírgula", "qualquer coisa parecida com um e-mail". O JavaScript oferece duas formas de criar uma regex:

index.js
Output
Click Run to see the output here.

A forma literal — com barras em volta do padrão e as flags logo depois da barra final — é o que você vai usar na maior parte do tempo. Deixe o new RegExp(...) para quando o próprio padrão for dinâmico, tipo quando você precisa montá-lo a partir da entrada do usuário ou de uma variável.

O i no final é uma flag. Ele indica que a busca ignora maiúsculas e minúsculas. Já já a gente fala mais sobre flags.

test: a string bate com o padrão?

A pergunta mais simples que dá para fazer a uma regex em JavaScript é: "essa string contém alguma ocorrência do padrão?". É aí que entra o test:

index.js
Output
Click Run to see the output here.

\d significa "qualquer dígito". O test devolve true ou false, e mais nada. Quando você só precisa de uma resposta do tipo sim ou não — validar um campo, filtrar um array — o test é a ferramenta certa.

match: extraindo o texto que casou

Quando o que você quer é o próprio texto que deu match, use o método match da string:

index.js
Output
Click Run to see the output here.

Sem a flag g, o match devolve um array com a primeira correspondência mais alguns metadados (index, input). Já com a flag g, ele retorna todas as ocorrências num array simples de strings. Quando nada bate com o padrão, o retorno é null — e não um array vazio —, então vale a pena se proteger contra isso:

index.js
Output
Click Run to see the output here.

Flags mudam o comportamento do pattern

As flags vêm depois da barra final e ajustam como o match acontece. As que você mais vai usar no dia a dia:

  • g — global, encontra todas as ocorrências em vez de parar na primeira.
  • i — ignora maiúsculas e minúsculas.
  • m — multilinha, faz ^ e $ casarem com início e fim de cada linha, e não só da string inteira.
  • s — dotall, permite que . também case com quebras de linha.
  • u — modo unicode, essencial para muitos emojis e caracteres fora do ASCII.
index.js
Output
Click Run to see the output here.

Sem o m, o ^ só casa com o início absoluto da string. Já com o m, ele passa a casar com o início de cada linha, então tanto Roses quanto Violets são capturados.

Classes de caracteres e quantificadores

Esses são os blocos básicos que formam a maioria das expressões regulares:

  • \d dígito, \w caractere de palavra (letra, dígito ou underscore), \s espaço em branco.
  • [abc] um dos caracteres a, b ou c. [^abc] qualquer coisa menos esses. [a-z] define um intervalo.
  • . qualquer caractere, exceto quebra de linha.
  • * zero ou mais, + um ou mais, ? zero ou um.
  • {3} exatamente três, {2,5} entre dois e cinco, {2,} dois ou mais.
  • ^ início, $ fim.

Juntando tudo:

index.js
Output
Click Run to see the output here.

\b é a borda de palavra — aquela linha invisível entre um caractere de palavra e um que não é. Serve bastante quando você quer casar a palavra inteira, sem pegar pedaços.

Grupos de captura: guardando partes do match

Os parênteses criam um grupo que captura o que foi encontrado ali dentro. Tanto exec quanto match devolvem essas capturas junto com o match principal:

index.js
Output
Click Run to see the output here.

O índice 0 guarda o match completo, e cada grupo ocupa uma posição própria depois dele. Ficar contando grupos pela posição vira uma dor de cabeça quando você tem mais de dois, então dê nomes a eles:

index.js
Output
Click Run to see the output here.

Grupos nomeados deixam o código mais legível na hora de usar e continuam funcionando mesmo se você reorganizar o padrão.

replace: reescrevendo o texto encontrado

O replace recebe um padrão e uma substituição. Essa substituição pode ser tanto uma string quanto uma função:

index.js
Output
Click Run to see the output here.

Sem a flag g, só a primeira ocorrência é substituída. Um erro clássico é esquecer dessa flag e ficar se perguntando por que o segundo e-mail continua errado.

A string de substituição aceita retrovisores (back-references). Use $1, $2 e assim por diante para se referir aos grupos de captura; para grupos nomeados, use $<nome>:

index.js
Output
Click Run to see the output here.

Para casos mais elaborados do que uma simples troca, passe uma função. Ela recebe o match e os grupos de captura como argumentos:

index.js
Output
Click Run to see the output here.

O underscore representa o match completo (que não precisamos aqui); n é o primeiro grupo de captura. Esse padrão — regex combinada com função substituta — resolve a maior parte das manipulações de texto do dia a dia.

matchAll: cada ocorrência com seus grupos de captura

O String.prototype.matchAll devolve um iterador com todos os matches junto com seus grupos de captura — algo que o match com a flag g sozinho não consegue entregar:

index.js
Output
Click Run to see the output here.

matchAll só funciona com a flag g. Sem ela, você toma um TypeError na cara. Se precisar acessar os resultados por índice em vez de iterar, use o spread para jogar tudo num array ([...text.matchAll(email)]).

Escapando caracteres especiais

Caracteres como . * + ? ( ) [ ] { } | \ ^ $ têm significado especial dentro de uma expressão regular. Para casar com eles de forma literal, basta escapar com uma contrabarra:

index.js
Output
Click Run to see the output here.

A versão sem escape casa com examplexcom, porque o . significa "qualquer caractere". Esse tipo de bug é comum — e passa despercebido. Se uma regex estiver casando demais, o primeiro suspeito é um . sem escape.

Quando você monta um padrão a partir de entrada do usuário, é obrigatório fazer o escape, senão o usuário pode injetar sintaxe de regex:

index.js
Output
Click Run to see the output here.

$& na string de substituição é um atalho que significa "a correspondência inteira".

Lookahead e lookbehind

Às vezes você quer casar um trecho de texto só quando ele vier seguido (ou precedido) de alguma coisa — sem incluir essa coisa no match. É exatamente isso que os lookarounds fazem:

index.js
Output
Click Run to see the output here.
  • (?= ...) lookahead positivo: "seguido por".
  • (?<= ...) lookbehind positivo: "precedido por".
  • (?! ...) e (?<! ...) são as versões negativas.

Os lookarounds não consomem caracteres, ou seja, o trecho que você está "olhando" continua disponível para a próxima parte do padrão.

Uma palavrinha sobre validação de e-mail

Esse tema aparece com frequência: "me passa uma regex pra validar e-mail". A resposta sincera é: não faça isso. A gramática real de e-mails é bem complicada, e qualquer regex curta o suficiente para ser lida vai estar errada em algum aspecto. Para validação de formulário, um padrão pragmático já resolve:

index.js
Output
Click Run to see the output here.

Leia assim: "alguns caracteres que não são espaço em branco nem @, um @, mais caracteres do mesmo tipo, um ponto, mais caracteres do mesmo tipo." Isso já pega os erros de digitação mais óbvios sem tentar bancar o RFC 5322. Para validação de verdade, mande um e-mail de confirmação.

Armadilhas comuns

Algumas pegadinhas que vale a pena gravar:

  • Esquecer a flag g no replace ou no matchAll. Você acaba pegando só a primeira ocorrência — ou toma um TypeError na cara.
  • lastIndex com estado em regex global. Uma regex com a flag g ou y guarda onde parou entre as chamadas de test/exec. Não reutilize a mesma instância em strings diferentes — crie uma nova ou use matchAll.
  • Pontos e barras sem escape em padrões dinâmicos. Sempre escape a entrada do usuário antes de jogar dentro de new RegExp(...).
  • Backtracking catastrófico. Quantificadores aninhados tipo (a+)+ em entradas maliciosas conseguem travar a aba inteira. Se uma regex parece lenta, simplifique.

A seguir: datas e horários

Regex cuida do formato do texto; mas dados reais também vêm com timestamps que precisam ser parseados, formatados e manipulados em cálculos. A próxima página mostra Date, Intl.DateTimeFormat e o modelo mental que evita dor de cabeça com fuso horário.

Perguntas frequentes

Como criar uma regex em JavaScript?

Tem dois jeitos. A forma literal usa barras: /hello/i. Já o construtor recebe uma string: new RegExp('hello', 'i'). Use a literal quando o padrão é fixo e o construtor quando precisar montar o padrão a partir de uma variável em tempo de execução.

Qual a diferença entre test, match e exec em regex no JavaScript?

regex.test(str) devolve um booleano — é o mais rápido quando você só quer saber se existe correspondência. str.match(regex) retorna um array com as correspondências (ou null). Já regex.exec(str) devolve uma correspondência por vez, com os grupos de captura, e, com a flag g, guarda a posição entre chamadas via lastIndex.

Como substituir todas as ocorrências com regex em JavaScript?

Use a flag g: str.replace(/foo/g, 'bar'). Sem o g, só a primeira ocorrência é trocada. Também dá pra chamar str.replaceAll(/foo/g, 'bar') — mas atenção: o replaceAll com regex exige a flag g, caso contrário ele lança um erro.

O que são grupos de captura na regex do JavaScript?

Parênteses dentro do padrão criam grupos de captura que guardam o que foi correspondido. /(\d{4})-(\d{2})/.exec('2024-11') retorna um array em que o índice 1 é '2024' e o índice 2 é '11'. Dá pra nomear os grupos com (?<year>\d{4}) e acessar via match.groups.year.

Aprenda a programar com o Coddy

COMEÇAR