
Expressões regulares na prática: guia com exemplos em JavaScript e Python
Uma expressão regular (regex) é um padrão que descreve um conjunto de textos: "quatro dígitos, um hífen, dois dígitos" é uma forma de descrever datas no formato 2026-10. Com ela, dá para validar formatos, encontrar trechos e fazer substituições em massa com uma linha de código. A sintaxe parece hostil no começo, mas o núcleo usado no dia a dia cabe em uma tabela. Os exemplos abaixo podem ser colados no console do navegador ou no interpretador Python.
Quando usar — e quando não usar
Regex brilha em textos com formato previsível: validar CEP ou placa, extrair campos de linhas de log, renomear padrões em vários arquivos, limpar espaços duplicados. Ela é a ferramenta errada para estruturas aninhadas, como HTML, JSON ou código-fonte: para isso existem parsers, que entendem a hierarquia do documento.
Também vale desconfiar de expressões que tentam validar tudo. O formato de endereços de e-mail definido nas RFCs é tão amplo que uma regex completa fica ilegível; na prática, uma checagem simples (algo@algo.algo) seguida de um e-mail de confirmação é mais confiável.
A sintaxe essencial em uma tabela
| Símbolo | Significado | Exemplo | Casa com |
|---|---|---|---|
| . | Qualquer caractere, exceto quebra de linha | a.c | abc, a-c, a7c |
| \d | Um dígito | \d{3} | 123 |
| \w | Letra, dígito ou _ (em JavaScript, só letras sem acento) | \w+ | nome_1 |
| \s | Espaço, tabulação ou quebra de linha | a\sb | a b |
| [abc] / [^abc] | Um dos caracteres / nenhum deles | [aeiou] | uma vogal |
| ^ e $ | Início e fim do texto (ou da linha, com a flag m) | ^oi$ | exatamente "oi" |
| * + ? | Zero ou mais / um ou mais / zero ou um | colou?r | color, colour |
| {n,m} | Entre n e m repetições | \d{2,4} | 12, 123, 1234 |
| ( ) | Grupo de captura | (\d{4})-(\d{2}) | captura ano e mês |
| (?: ) | Grupo sem captura | (?:ab)+ | abab |
| (?<nome> ) | Grupo nomeado (em Python: (?P<nome> )) | (?<ano>\d{4}) | ano |
| | | Alternativa | gato|cão | gato ou cão |
| \b | Fronteira de palavra | \bsol\b | sol, mas não solar |
Uma diferença que pega muita gente: em JavaScript, \w e \b consideram apenas letras ASCII, então "ação" não é uma palavra inteira para eles. Em Python 3, com textos str, \w reconhece letras acentuadas. Para textos em português no JavaScript, use classes explícitas como [A-Za-zÀ-ÿ] ou a flag u com \p{L}.
Exemplos em JavaScript
// 1. Extrair uma data AAAA-MM-DD com grupos nomeados
const re = /(?<ano>\d{4})-(?<mes>\d{2})-(?<dia>\d{2})/;
const m = "Publicado em 2026-10-29".match(re);
console.log(m.groups.dia, m.groups.mes, m.groups.ano); // 29 10 2026
// 2. Reformatar todas as datas para DD/MM/AAAA (flag g = global)
const texto = "Início 2026-01-05, fim 2026-03-20";
console.log(texto.replace(/(\d{4})-(\d{2})-(\d{2})/g, "$3/$2/$1"));
// "Início 05/01/2026, fim 20/03/2026"
// 3. Percorrer linhas de log com matchAll (exige a flag g; m faz ^ e $ valerem por linha)
const log = "GET /api 200\nPOST /login 401\nGET /home 200";
for (const [, metodo, rota, status] of log.matchAll(/^(\w+) (\S+) (\d{3})$/gm)) {
if (status !== "200") console.log(metodo, rota, status); // POST /login 401
}
// 4. CEP: 5 dígitos, hífen opcional, 3 dígitos — ^ e $ impedem sobras
const cep = /^\d{5}-?\d{3}$/;
console.log(cep.test("01310-100"), cep.test("01310100"), cep.test("0131-0100"));
// true true false
// 5. Letras com acento: \p{L} com a flag u
console.log("ação rápida".match(/\p{L}+/gu)); // ["ação", "rápida"]Cuidado com regex que tem a flag g e é reutilizada com test(): ela guarda a posição da última busca em lastIndex, e a chamada seguinte começa dali — um resultado alternando entre true e false costuma ser isso. Para validação, não use g.
Os mesmos exemplos em Python
import re
# Grupos nomeados usam a sintaxe (?P<nome>...)
m = re.search(r"(?P<ano>\d{4})-(?P<mes>\d{2})-(?P<dia>\d{2})", "Publicado em 2026-10-29")
print(m["dia"], m["mes"], m["ano"]) # 29 10 2026
# Substituição com referência aos grupos: \3/\2/\1
texto = "Início 2026-01-05, fim 2026-03-20"
print(re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\3/\2/\1", texto))
# Início 05/01/2026, fim 20/03/2026
# Compile quando o padrão for reutilizado
LINHA = re.compile(r"^(\w+) (\S+) (\d{3})$", re.MULTILINE)
log = "GET /api 200\nPOST /login 401\nGET /home 200"
for metodo, rota, status in LINHA.findall(log):
if status != "200":
print(metodo, rota, status) # POST /login 401
# fullmatch exige que o texto inteiro case, sem precisar de ^ e $
print(bool(re.fullmatch(r"\d{5}-?\d{3}", "01310-100"))) # TrueEm Python, escreva padrões sempre como raw strings (r"..."). Sem o r, "\b" vira o caractere de backspace antes mesmo de chegar ao módulo re, e a expressão para de funcionar sem nenhum erro aparente.
Ganancioso versus preguiçoso
Por padrão, os quantificadores * e + são gananciosos: casam o máximo possível. Acrescentar ? depois deles os torna preguiçosos, casando o mínimo. A diferença aparece logo em textos com delimitadores repetidos:
const html = "<b>negrito</b> e <i>itálico</i>";
console.log(html.match(/<.+>/)[0]); // "<b>negrito</b> e <i>itálico</i>" (ganancioso)
console.log(html.match(/<.+?>/)[0]); // "<b>" (preguiçoso)
console.log(html.match(/<[^>]+>/g)); // ["<b>", "</b>", "<i>", "</i>"]A terceira forma — uma classe negada, [^>]+ — costuma ser a melhor: diz exatamente o que pode aparecer entre os delimitadores e evita retrocessos desnecessários do motor de busca.
Desempenho e segurança: o risco de ReDoS
Os motores de regex de JavaScript, Python, Java e PCRE usam retrocesso (backtracking): quando um caminho falha, voltam e tentam outro. Com quantificadores aninhados, como em ^(a+)+$, uma entrada que quase casa — muitos "a" seguidos de um "!" — obriga o motor a testar um número de combinações que cresce exponencialmente. Uma única requisição com esse texto pode travar um servidor Node.js, já que o event loop fica preso. Esse ataque é chamado de ReDoS (Regular expression Denial of Service).
- Evite quantificadores aninhados e alternativas que se sobrepõem, como (a|a)+ ou (\w+\s?)+.
- Limite o tamanho da entrada antes de aplicar a regex em dados vindos do usuário.
- Prefira classes específicas ([^,]+) a curingas genéricos (.*) dentro de repetições.
- Para padrões vindos de usuários, use um motor de tempo linear: o pacote regexp da biblioteca padrão de Go e a biblioteca RE2 (com bindings para Node.js e Python) garantem tempo proporcional ao tamanho da entrada, em troca de não suportar alguns recursos como retrovisores.
Como testar e manter expressões regulares
Sites como o regex101 mostram, passo a passo, o que cada parte do padrão está casando e permitem escolher o dialeto (JavaScript, Python, PCRE). Mas o teste que conta é o automatizado: para cada regex importante, escreva casos que devem casar e casos que não devem — inclusive os de borda, como string vazia e espaços nas pontas. Em Python, a flag re.VERBOSE permite quebrar o padrão em várias linhas com comentários, o que ajuda muito quem for mantê-lo depois.
Conclusão
Comece pelo núcleo — classes (\d, \s, colchetes), quantificadores, âncoras e grupos — e pratique em problemas reais, como extrair dados de um log. Use âncoras em validações, raw strings em Python, cuidado com a flag g em JavaScript e desconfiança com quantificadores aninhados. E quando o texto tiver estrutura aninhada, troque a regex por um parser.
Perguntas frequentes
+Regex é igual em todas as linguagens?
O núcleo (classes, quantificadores, âncoras, grupos) é praticamente o mesmo. Mudam detalhes: a sintaxe de grupos nomeados ((?<nome>) em JavaScript, (?P<nome>) em Python), o suporte a Unicode em \w e recursos avançados como lookbehind e retrovisores. Ferramentas como o regex101 permitem testar no dialeto certo.
+Como validar e-mail com regex?
Use uma checagem simples, como ^[^\s@]+@[^\s@]+\.[^\s@]+$, apenas para pegar erros de digitação óbvios, e confirme o endereço enviando um e-mail. Regex nenhuma garante que o endereço existe ou pertence à pessoa.
+Qual a diferença entre test, match e matchAll no JavaScript?
test() devolve apenas true ou false. match() sem a flag g devolve a primeira ocorrência com os grupos; com g, devolve só os textos encontrados, sem grupos. matchAll() exige a flag g e devolve todas as ocorrências com seus grupos, o que costuma ser o mais útil para extração.
+Expressões regulares deixam o código lento?
Na maioria dos casos, não: para textos curtos e padrões simples, a execução leva microssegundos. O problema aparece com quantificadores aninhados em entradas grandes ou maliciosas (ReDoS) e ao recompilar o padrão dentro de laços longos — em Python, use re.compile; em JavaScript, declare a regex fora do laço.
Fontes consultadas
- MDN — Guia de expressões regulares (JavaScript)
- Python — documentação do módulo re
- Python — Regular Expression HOWTO
- OWASP — Regular expression Denial of Service (ReDoS)
- Go — pacote regexp (motor RE2, tempo linear)
Histórico: publicado em . Conteúdo educativo, sem patrocínio das ferramentas citadas. Encontrou um erro? Envie uma correção.
Leia também

Cache em aplicações web: HTTP, CDN e Redis — onde guardar cada dado
Como funcionam as camadas de cache de uma aplicação web, quais cabeçalhos Cache-Control usar para cada tipo de recurso, o padrão cache-aside com Redis e como evitar dados velhos e vazamentos.

Serverless: quando funções serverless valem a pena (e quando viram armadilha)
AWS Lambda, Cloudflare Workers e Vercel Functions comparados: modelo de custo, cold starts, limites de execução e os workloads onde serverless brilha ou falha.

Core Web Vitals na prática: LCP, INP e CLS medidos e corrigidos
Guia técnico dos três Core Web Vitals: como medir com dados reais (CrUX/RUM), as causas mais comuns de LCP, INP e CLS ruins e as correções que funcionam.