10 Truques de Regex que Todo Desenvolvedor Deve Conhecer
Expressões regulares são aquela ferramenta que parece impenetrável no início e depois de repente faz sentido. Quando isso acontece, você começa a vê-las em todo lugar: validação de entrada, análise de logs, pipelines de busca e substituição, roteamento de URLs. Mas a maioria dos desenvolvedores usa apenas um punhado de recursos — classes de caracteres, quantificadores, âncoras — e deixa o resto da especificação intocada.
Este guia cobre dez recursos de regex que vão além do básico. Cada um resolve um problema real que padrões mais simples não conseguem tratar de forma limpa. Todos os exemplos usam sintaxe JavaScript, que também é válida para qualquer ambiente compatível com ECMAScript.
Você pode testar cada padrão deste artigo usando o Toova Regex Tester sem escrever uma única linha de configuração.
1. Lookaheads: Casar Sem Consumir
Um lookahead afirma que um padrão deve (ou não deve) seguir a posição atual, sem fazer o motor de captura avançar além desses caracteres. O texto capturado não inclui o que o lookahead verifica.
Sintaxe de lookahead positivo: (?=...)
// Lookahead positivo: captura "foo" apenas quando seguido de "bar"
const re1 = /foo(?=bar)/;
re1.test('foobar'); // true
re1.test('foobaz'); // false
Sintaxe de lookahead negativo: (?!...)
// Lookahead negativo: captura "foo" quando NÃO seguido de "bar"
const re2 = /foo(?!bar)/;
re2.test('foobaz'); // true
re2.test('foobar'); // false
Um uso prático: capturar um número de preço apenas quando seguido de um símbolo de moeda, sem incluir o símbolo no valor capturado. Ou validar que uma senha contém pelo menos um dígito usando (?=.*\d) sem especificar onde o dígito deve aparecer.
Lookaheads têm largura zero — não consomem caracteres. Você pode empilhar múltiplos lookaheads na mesma posição para impor várias condições independentes simultaneamente.
2. Lookbehinds: Verificar o que Veio Antes
Um lookbehind é o espelho de um lookahead: verifica o texto que precede a posição atual sem incluí-lo na captura.
Sintaxe de lookbehind positivo: (?<=...)
// Lookbehind positivo: captura "bar" apenas quando precedido de "foo"
const re3 = /(?<=foo)bar/;
re3.test('foobar'); // true
re3.test('bazbar'); // false
Sintaxe de lookbehind negativo: (?<!...)
// Lookbehind negativo: captura "bar" quando NÃO precedido de "foo"
const re4 = /(?<!foo)bar/;
re4.test('bazbar'); // true
re4.test('foobar'); // false
Lookbehinds chegaram ao ECMAScript 2018 e são suportados em todos os navegadores modernos e Node.js 10+. Um caso de uso comum: extrair a parte do valor de um par chave-valor como nome=Alice capturando tudo após nome= sem incluir a chave na captura.
Atenção: ao contrário dos lookaheads, expressões de lookbehind em JavaScript não podem conter padrões de comprimento variável — a expressão lookbehind deve ter comprimento fixo ou máximo limitado.
3. Grupos de Captura Nomeados: Padrões Autodocumentados
Grupos de captura padrão são referenciados por número: $1, $2 e assim por diante. Quando você adiciona ou remove um grupo, toda referência posterior quebra. Grupos nomeados resolvem isso permitindo anexar um rótulo a cada grupo.
const dateRe = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const m = '2026-05-10'.match(dateRe);
console.log(m.groups.year); // "2026"
console.log(m.groups.month); // "05"
console.log(m.groups.day); // "10"
Grupos nomeados também estão disponíveis em strings de substituição via $<nome>:
// Retrorreferência usando grupo nomeado no padrão
const quoteRe = /(?<q>['"]).*?\k<q>/;
quoteRe.test('"hello"'); // true
quoteRe.test('"hello''); // false
O nome do grupo deve ser um identificador JavaScript válido. Use nomes descritivos que reflitam o que o grupo captura — year, port, protocol — e seus padrões se tornam quase autodocumentados. Você também pode usar \k<nome> dentro do próprio padrão para referenciar um grupo nomeado, como mostrado acima.
4. Quantificadores Non-Greedy: Casar o Mínimo
Por padrão, quantificadores (*, +, ?) são greedy — casam o máximo de caracteres possível. Adicionar um ? após o quantificador o torna non-greedy (também chamado de lazy ou relutante), casando o mínimo de caracteres possível.
const html = '<a>clique</a>';
// Greedy (padrão) — captura a string mais longa possível
/<.+>/.exec(html)?.[0]; // '<a>clique</a>'
// Non-greedy — captura a string mais curta possível
/<.+?>/.exec(html)?.[0]; // '<a>' Isso importa ao analisar HTML, XML ou qualquer formato onde o mesmo delimitador pode aparecer várias vezes. A versão greedy consome tudo desde a primeira abertura até o último fechamento em toda a string. A versão non-greedy para no primeiro fechamento válido.
O mesmo se aplica a +? (um ou mais, lazy) e ?? (zero ou um, lazy). Quantificadores non-greedy não mudam o que pode ser capturado — mudam qual captura válida é selecionada quando existem múltiplas opções.
5. Evitando Backtracking Catastrófico
Backtracking é como os motores de regex se recuperam de uma tentativa de captura fracassada — tentam um caminho diferente pelo padrão. Na maioria dos casos é invisível e rápido. Mas certos padrões podem fazer o motor explorar um número exponencialmente crescente de caminhos, derrubando um processo Node.js mesmo com uma string de entrada modesta.
O padrão clássico de perigo são quantificadores aninhados como (a+)+ aplicado a uma string como aaaaab. O motor tenta todas as formas possíveis de dividir os caracteres a entre os grupos interno e externo antes de concluir que não há captura.
Grupos atômicos ((?>...)) impedem isso dizendo ao motor para não fazer backtracking em um grupo após ele ter casado. JavaScript não suporta grupos atômicos nativamente, mas você pode emular comportamento possessivo com um lookahead:
// Sem grupo atômico — o motor faz backtracking em (\d+)
// Com grupo atômico — após (\d+) casar, não há mais backtracking
// JavaScript não suporta grupos atômicos nativamente,
// mas você pode emulá-los com um lookahead:
const re5 = /(?=(\d+))\1(?!\d)/; // emula quantificador possessivo \d++ A regra mais segura: evite quantificadores diretamente aninhados dentro de outros quantificadores, a menos que haja um motivo específico. Reescreva padrões para ser mais preciso sobre o que casam. Você também pode usar a ferramenta Text Diff para comparar a saída de dois padrões equivalentes lado a lado ao refatorar.
6. Operações de Conjunto em Classes de Caracteres (Flag Unicode v)
O ECMAScript 2024 introduziu a flag v, que habilita operações de conjunto dentro de classes de caracteres. Isso permite expressar "todas as letras exceto vogais" ou "letras maiúsculas que também são ASCII" como uma definição de classe limpa em vez de uma alternância complicada.
// Subtração de classes de caractere POSIX não existe no JS,
// mas o modo Unicode sets (flag `v`) adiciona operações de conjunto:
const lettersNoVowels = /[a-z--[aeiou]]/v;
lettersNoVowels.test('b'); // true
lettersNoVowels.test('e'); // false
A flag v suporta três operações dentro de classes de caracteres:
- Subtração:
[A--B]— caracteres em A mas não em B - Interseção:
[A&&B]— caracteres em ambos A e B - União:
[AB]— caracteres em A ou B (igual às classes de caracteres padrão)
Node.js 20+ e todos os navegadores evergreen suportam a flag v. É um superconjunto da flag u — não combine ambas; use v sozinha quando precisar de seus recursos.
7. Fronteiras de Palavra: Captura de Palavras Inteiras
A âncora \b casa a posição entre um caractere de palavra (\w) e um caractere que não é de palavra. Não consome caracteres — apenas afirma a posição. Seu inverso \B casa qualquer posição que não seja uma fronteira de palavra.
const sentence = 'cat concatenate';
// Sem \b — "cat" também é encontrado dentro de "concatenate"
/cat/g.exec(sentence); // casa "cat" em "cat" E em "concatenate"
// Com \b — apenas a palavra inteira "cat"
/\bcat\b/g.exec(sentence); // casa somente o "cat" isolado // \B é o inverso: casa dentro de uma palavra, não na fronteira
/\Bcat\B/.test('concatenate'); // true — "cat" está dentro da palavra
Fronteiras de palavra são essenciais ao pesquisar identificadores em código ou prosa. Sem elas, pesquisar por uma variável chamada id também encontraria indexOf, invalid e grid. Use \btermo\b para restringir capturas a ocorrências isoladas.
Um aviso importante: \b usa a definição JavaScript de caractere de palavra ([a-zA-Z0-9_]). Caracteres acentuados e letras não latinas são tratados como caracteres que não são de palavra. Para fronteiras de palavra com suporte Unicode, combine a flag v com classes de propriedade Unicode.
8. Modo Multilinha: Âncoras por Linha
Por padrão, ^ casa apenas o início absoluto da string e $ casa apenas o fim absoluto. A flag m (multilinha) muda isso: ^ casa o início de cada linha e $ casa o fim de cada linha.
const text = 'linha um\nlinha dois\nlinha três';
// Sem flag m — ^ só casa o início da string inteira
/^linha/.test(text); // true (somente a primeira linha)
// Com flag m — ^ casa o início de CADA linha
const matches = text.match(/^linha/gm);
console.log(matches); // ['linha', 'linha', 'linha'] Isso é indispensável ao processar texto de múltiplas linhas como arquivos de log, arquivos de configuração ou código. Usos comuns incluem extrair linhas que começam com uma palavra-chave, substituir tokens de fim de linha ou validar que cada linha em um bloco corresponde a um padrão.
Não confunda a flag m com a flag s (dotAll). A flag s faz o . casar caracteres de nova linha também. A flag m não afeta o . de jeito nenhum — apenas o comportamento de ^ e $.
9. Escapes de Propriedade Unicode: Captura Internacional de Caracteres
A flag u habilita escapes de propriedade Unicode, que permitem casar caracteres com base em sua categoria Unicode, script ou outra propriedade. Esta é a forma correta de casar letras, dígitos ou pontuação em todos os sistemas de escrita humanos — não apenas ASCII.
// A flag u habilita escapes de propriedade Unicode
const letters = /\p{L}+/u;
letters.test('Héllo'); // true
letters.test('你好'); // true
letters.test('12345'); // false
// Captura apenas letras maiúsculas em todos os scripts
const upper = /\p{Lu}+/u;
upper.test('ABC'); // true
upper.test('abc'); // false // Captura emoji (categoria Unicode geral: Symbol, Other)
const emoji = /\p{So}/u;
emoji.test('🚀'); // true As propriedades Unicode mais usadas são:
\p{L}— qualquer letra (todos os scripts)\p{Lu}— letras maiúsculas\p{Ll}— letras minúsculas\p{N}— qualquer número\p{Nd}— dígitos decimais\p{P}— pontuação\p{Script=Latin}— caracteres do script latino\p{Emoji}— caracteres emoji
Use \P{...} (P maiúsculo) para negar — casando tudo que não tem a propriedade especificada. A lista completa de propriedades Unicode suportadas e seus valores é mantida na documentação do MDN.
10. Padrões Verbosos via Montagem de Strings
Muitas linguagens (Python, Ruby, .NET, PCRE) suportam um modo verbose ou estendido (flag x) que permite espaços em branco e comentários dentro de padrões. JavaScript não tem essa flag — a flag x não é válida no ECMAScript.
A solução padrão é montar padrões a partir de constantes de string nomeadas e combiná-las com new RegExp():
// JavaScript não tem flag x nativa,
// mas você pode montar padrões legíveis como constantes de string:
const YEAR = '(?<year>\\d{4})';
const SEP = '-';
const MONTH = '(?<month>\\d{2})';
const DAY = '(?<day>\\d{2})';
const datePattern = new RegExp(YEAR + SEP + MONTH + SEP + DAY); Cada constante descreve o que ela casa, e o padrão final se lê como uma sentença. Essa abordagem também facilita a composição de subpadrões compartilhados entre múltiplas definições de regex em uma base de código e o teste unitário de cada componente independentemente.
Para padrões menos complexos, manter o regex inteiro em uma linha com comentários inline em um bloco de comentário próximo geralmente é suficiente. O objetivo é garantir que o próximo desenvolvedor (incluindo você no futuro) possa entender o padrão sem precisar decifrá-lo.
Colocando Tudo em Prática
Essas dez técnicas cobrem uma grande parte da superfície de "por que esse regex falha em casos extremos?". Lookaheads e lookbehinds permitem afirmar contexto sem consumi-lo. Grupos nomeados mantêm padrões legíveis durante refatorações. Quantificadores non-greedy evitam capturas acidentais em excesso. Escapes de propriedade Unicode lidam com entrada que vai além do ASCII.
A melhor forma de ganhar fluência é experimentar com padrões reais contra dados reais. Use o Regex Tester para iterar rapidamente. Quando seu padrão produzir saída que precisa de comparação ou limpeza, a ferramenta Text Diff mostra exatamente o que mudou entre execuções. E se seu regex estiver analisando JSON, o JSON Formatter permite inspecionar o resultado estruturado sem sair do navegador.
Para uma referência completa de toda a sintaxe e flags de regex JavaScript, o Cheatsheet de Regex do MDN é a melhor página única para ter nos favoritos. Para depuração interativa de padrões com visualização completa de capturas, regex101.com suporta o modo JavaScript com uma explicação integrada de cada componente do padrão.