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:
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:
\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:
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:
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.
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:
\ddígito,\wcaractere de palavra (letra, dígito ou underscore),\sespaç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:
\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:
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:
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:
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>:
Para casos mais elaborados do que uma simples troca, passe uma função. Ela recebe o match e os grupos de captura como argumentos:
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:
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:
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:
$& 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:
(?= ...)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:
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
gnoreplaceou nomatchAll. Você acaba pegando só a primeira ocorrência — ou toma umTypeErrorna cara. lastIndexcom estado em regex global. Uma regex com a flaggouyguarda onde parou entre as chamadas detest/exec. Não reutilize a mesma instância em strings diferentes — crie uma nova ou usematchAll.- 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.