Expressões Regulares (Regex): Guia Completo 2026 com Exemplos Práticos
Regex é uma das habilidades com maior ROI na carreira de desenvolvimento: aprender os fundamentos em algumas horas economiza centenas de linhas de código ao longo do tempo. Este guia cobre do metacaractere básico ao lookbehind, com exemplos prontos em JavaScript e Python.
Por Vitor Morais
Fundador do MochaLabz ·
Teste regex em tempo real
Cole o padrão, cole o texto e veja os matches destacados na hora.
Usar testador →Expressões regulares, ou regex, são um mini-idioma para descrever padrões em texto. Com uma única expressão você busca, valida, substitui e extrai dados de qualquer tipo de string — de logs de servidor a formulários web, de CSVs mal formatados a código-fonte. É conhecimento que, uma vez instalado na cabeça, acompanha você em qualquer linguagem e qualquer editor pelo resto da carreira.
A fama de “esotérico” vem de tutoriais que despejam 50 símbolos na primeira página. Ignorado isso, regex é pequeno e lógico. Este guia constrói do fundamento até técnicas avançadas (lookaround, grupos nomeados, backreferences), sempre com caso real.
O modelo mental: regex como linguagem de busca
Uma regex é um padrão. Quando aplicada a um texto, o engine percorre caractere por caractere tentando casar o padrão. Se casa, retorna o match; se não, segue adiante. Três operações básicas se constroem sobre esse mecanismo:
- Teste: o texto contém o padrão?
- Extração: quais partes do texto casam com o padrão?
- Substituição: troque o que casou por outra coisa.
Toda regex que você vai escrever cai em uma dessas três categorias.
Caracteres literais e metacaracteres
Letras e números em regex casam consigo mesmos. gato bate com “gato” em qualquer texto. Os metacaracteres são os que têm significado especial:
| Critério | Significado | Exemplo |
|---|---|---|
| . | Qualquer caractere (exceto quebra de linha) | c.sa bate com casa, cosa, cxsa |
| ^ | Início de linha/string | ^abc bate com linha que começa em abc |
| $ | Fim de linha/string | xyz$ bate com linha que termina em xyz |
| \d | Dígito (0-9) | \d\d bate 42, 07, 99 |
| \w | Letra, número ou _ | \w+ bate palavras |
| \s | Espaço em branco | a\sb bate 'a b' ou 'a\tb' |
| \D \W \S | Negações de \d \w \s | \D bate qualquer não-dígito |
| \b | Borda de palavra | \bfoo\b bate foo isolado |
Classes de caracteres
Dentro de [] você lista caracteres aceitos. Dentro do colchete, a maioria dos metacaracteres perde o sentido especial.
[abc]bate com a, b ou c.[a-z]bate com qualquer letra minúscula.[A-Za-z0-9]bate com letras e números.[^abc]bate com qualquer coisa que não seja a, b ou c (negação com^no início).
// JavaScript: extrair apenas números de um texto
const texto = "Pedido #4823 foi aprovado com valor R$ 1250.";
const numeros = texto.match(/\d+/g);
// ["4823", "1250"]
// Python equivalente
import re
re.findall(r"\d+", "Pedido #4823 foi aprovado com valor R$ 1250.")
# ['4823', '1250']Quantificadores: quantas vezes o padrão repete
| Critério | Significa | Exemplo |
|---|---|---|
| ? | 0 ou 1 vez (opcional) | colou?r bate color e colour |
| * | 0 ou mais vezes | a* bate vazio, a, aa, aaa... |
| + | 1 ou mais vezes | \d+ bate 1, 42, 99999 |
| {n} | Exatamente n vezes | \d{4} bate 4 dígitos seguidos |
| {n,} | n ou mais vezes | \d{3,} bate 3+ dígitos |
| {n,m} | Entre n e m vezes | \d{2,4} bate 2 a 4 dígitos |
Atenção
?: .*?, \d+?. A diferença entre <.*> e <.*?> em HTML é o bug mais famoso de regex do mundo.Alternação e grupos
A barra vertical | funciona como “ou”. Parênteses () agrupam partes do padrão e também criam grupos de captura.
// Alternação: casa com gato ou cachorro
/gato|cachorro/
// Agrupar para aplicar quantificador em bloco inteiro
/(ab)+/ // ab, abab, ababab
// Captura: parênteses criam grupos acessíveis
const match = "João nasceu em 1985".match(/(\w+).*?(\d{4})/);
// match[1] === "João"
// match[2] === "1985"
// Non-capturing group: agrupa sem guardar
/(?:ab)+/ // agrupa para o +, mas não cria grupoGrupos nomeados e backreferences
Em padrões complexos, nomear grupos torna o código muito mais legível:
// Grupo nomeado (JavaScript ES2018+)
const match = "2026-04-17".match(
/(?<ano>\d{4})-(?<mes>\d{2})-(?<dia>\d{2})/
);
// match.groups.ano === "2026"
// match.groups.mes === "04"
// match.groups.dia === "17"
// Backreference: referencia grupo já capturado
// Detecta palavras duplicadas:
/(\b\w+) \1/
// bate "o o", "muito muito", "bem bem"Dica
$1, $2 ou $<nome> (para grupos nomeados). Útil para reformatar datas: 2026-04-17 → 17/04/2026 em uma linha.Lookahead e lookbehind
Lookaround testa se algo vem antes ou depois sem consumir o match. Útil para capturar “X que vem antes de Y” ou “Z que não é seguido por W”.
| Critério | Sintaxe | Significa |
|---|---|---|
| Positive lookahead | (?=...) | o match precisa ser seguido por ... |
| Negative lookahead | (?!...) | o match NÃO pode ser seguido por ... |
| Positive lookbehind | (?<=...) | o match precisa ser precedido por ... |
| Negative lookbehind | (?<!...) | o match NÃO pode ser precedido por ... |
// Extrair preço em reais (número seguido de "reais" sem
// capturar a palavra "reais")
const texto = "Custa 120 reais e vale 200 reais";
texto.match(/\d+(?= reais)/g);
// ["120", "200"]
// Capturar @username em menções, sem o @
"oi @joao e @maria".match(/(?<=@)\w+/g);
// ["joao", "maria"]
// Senhas que têm maiúscula, minúscula, número e 8+ chars
const forte = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;Flags: o comportamento global
g(global): encontra todos os matches, não só o primeiro.i(insensitive): ignora maiúsculas e minúsculas.m(multiline): faz^e$casarem início e fim de linha, não só da string inteira.s(dotall): faz.bater também em quebras de linha.u(unicode): suporta escapes unicode e grupos de propriedade.y(sticky, JavaScript): match ancorado na posiçãolastIndex.
Padrões prontos para copiar
E-mail simples (80% dos casos)
/^[\w.+-]+@[\w-]+\.[\w.-]+$/URL (http/https)
/https?:\/\/[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=%]*/Telefone brasileiro (celular e fixo)
/^\(?\d{2}\)?[\s-]?\d{4,5}[\s-]?\d{4}$/CPF com ou sem pontuação
/^\d{3}\.?\d{3}\.?\d{3}-?\d{2}$/CEP brasileiro
/^\d{5}-?\d{3}$/Data ISO (YYYY-MM-DD)
/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/Senha forte (8+ chars, maiúscula, minúscula, número)
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/Aplicação prática em JavaScript
const regex = /\d+/g;
const texto = "Pedido 42 e pedido 107";
// Teste
regex.test(texto); // true
// Extração
texto.match(regex); // ["42", "107"]
[...texto.matchAll(regex)]; // array com índices
// Substituição
texto.replace(regex, "N"); // "Pedido N e pedido N"
// Split
"um,dois;tres quatro".split(/[,;\s]+/);
// ["um", "dois", "tres", "quatro"]Aplicação em Python
import re
texto = "Pedido 42 e pedido 107"
# Teste
bool(re.search(r"\d+", texto)) # True
# Extração
re.findall(r"\d+", texto) # ['42', '107']
# Iteração com objetos match
for m in re.finditer(r"\d+", texto):
print(m.group(), m.start(), m.end())
# Substituição
re.sub(r"\d+", "N", texto) # "Pedido N e pedido N"
# Compilar uma vez, usar várias (mais rápido em loop)
padrao = re.compile(r"\d+")
padrao.findall(texto)Erros clássicos que geram bug em produção
- Greedy onde devia ser lazy:
<.*>em HTML captura tudo; use<.*?>. - Esquecer ancoragem: validar CPF sem
^e$aceita123.456.789-00 lixo extra. - Usar
.achando que exclui quebras: em flagsou em alguns engines, casa com qualquer coisa. - Não testar input vazio: regex com
*pode match string vazia. - Não escapar caracteres especiais em valor dinâmico: construir regex com input do usuário é vetor de ReDoS.
- Ignorar unicode:
\wem JavaScript só bate ASCII. Use\p{L}com flagupara letras acentuadas.
Performance: quando regex fica lenta
Regex é rápida para a maioria dos casos, mas algumas construções são armadilha:
- Backtracking catastrófico: padrões com quantificadores aninhados (
(.+)+) em input grande travam o engine. - Alternação mal ordenada:
(a|ab|abc)é mais lenta que(abc|ab|a)porque o engine tenta matches em ordem. - Regex compilada a cada iteração: em Python e JavaScript, se você usa a mesma regex em loop, compile uma vez fora.
Vai mais fundo
Quando regex é a ferramenta errada
- Parsear HTML, XML ou JSON — sempre use parser dedicado.
- Cálculo de validação (CPF, cartão de crédito) — regex vê formato, não aritmética.
- Buscas triviais de substring —
includesouindexOfé mais legível. - Lógica de negócio complexa — 50 alternações num único regex é manutenção impossível.
Estratégia para escrever regex que dura
- Comece escrevendo em prosa: “4 dígitos, hífen, 2 dígitos, hífen, 2 dígitos”.
- Traduza bloco a bloco:
\d{4}-\d{2}-\d{2}. - Teste com 3 inputs: válido típico, válido edge, inválido.
- Cole no regex101, leia a explicação gerada. Se não bate com o que você queria, refaça.
- Adicione ancoragem e refine progressivamente.
- Comente o regex no código — um regex complexo sem comentário é sempre dívida técnica.
Regex em uma frase
Expressões regulares são um investimento raro em dev: algumas horas de estudo rendem décadas de produtividade, em qualquer linguagem, qualquer editor, qualquer linha de comando. Aprenda os 20 metacaracteres essenciais, domine os quantificadores, entenda grupos — e o resto é só composição.
Perguntas frequentes
Regex é a mesma coisa em JavaScript, Python, PHP e outras linguagens?+
O núcleo é igual: metacaracteres, quantificadores, classes e grupos seguem a mesma gramática. As diferenças estão nas flags (JavaScript usa /g, Python usa re.MULTILINE), no suporte a recursos avançados (PHP e Python têm lookbehind variável; JavaScript até 2018 não tinha lookbehind) e na sintaxe para acessar grupos. 90% do que você aprende em uma linguagem se aplica em qualquer outra.
Quando não usar regex?+
Para parsear HTML, JSON, XML ou qualquer formato com estrutura aninhada. Regex não reconhece hierarquia recursiva — use o parser dedicado (DOMParser, JSON.parse, libxml). Para validação de e-mail rigorosa, use a biblioteca da linguagem (ex.: email-validator em Python), que implementa RFC 5322 corretamente. Para busca simples em texto pequeno, indexOf ou includes é mais legível.
Qual a diferença entre .* e .*?+
O .* é greedy (ganancioso): captura o maior match possível. O .*? é lazy (preguiçoso): captura o menor match possível. Em texto <b>um</b> <b>dois</b>, o padrão <b>.*</b> captura tudo de uma vez; <b>.*?</b> captura cada bloco separadamente. 90% dos bugs de regex vêm de confundir os dois.
Preciso escapar barras, parênteses e outros caracteres especiais?+
Sim, quando você quer que o caractere apareça literal e não como metacaractere. Para cuidar dos comuns, escape com barra invertida: \. \( \) \? \+ \*. Dentro de classes [], a maioria dos metacaracteres perde o sentido especial e não precisa escapar — exceção: ], \ e, em algumas posições, - e ^.
O que é ReDoS e como evitar?+
Regex Denial of Service é ataque em que um regex mal escrito trava o servidor. Acontece com padrões catastróficos como (a+)+$ rodando em input aaaaaaaaaaX — o engine tenta bilhões de combinações. Evitar: desconfie de quantificadores aninhados, use possessive quantifiers ou atomic groups quando disponíveis, valide tamanho máximo do input antes de aplicar regex, e prefira regex simples a virtuosismo.
Qual ferramenta usar para testar regex antes de colocar em produção?+
Regex101.com é o padrão da indústria. Mostra explicação em tempo real, destaca matches, simula todas as flags, tem debugger passo a passo e permite salvar. Alternativas: Regexr (mais leve), regexper.com (gera diagrama visual do padrão). Sempre teste com pelo menos 3 casos: input típico, input vazio e input malicioso/extremo.
É possível fazer regex case-insensitive?+
Sim. Em JavaScript e PCRE, adicione a flag i: /hello/i bate com HELLO, Hello, hello. Em Python, passe re.IGNORECASE como segundo argumento. Também dá para usar inline modifiers: (?i)hello ativa case-insensitive dentro do próprio padrão, o que é útil quando você não controla o compile flags.
Regex substitui validação de negócio como CPF e CNPJ?+
Parcialmente. Regex valida o formato (tamanho, separadores, tipo de caractere), mas não o algoritmo do dígito verificador. Para CPF e CNPJ brasileiros, o regex filtra entrada inválida na UI, mas a validação final exige função que calcula módulo 11 e compara com os dois últimos dígitos. Use regex como primeira barreira e o algoritmo como confirmação.
Artigos relacionados
O que Torna um E-mail Válido? Regras Técnicas e Validação (2026)
Estrutura conforme RFC 5321/5322, caracteres permitidos, exemplos surpreendentes, regex balanceada vs literal, validação por DNS/SMTP, IDN/Unicode e detecção de e-mails descartáveis.
Como Ler CSV em JavaScript (2026): FileReader, Node.js, Streams e PapaParse
Guia completo de leitura de CSV em JavaScript: FileReader no browser, csv-parse e streams no Node.js, PapaParse para grandes volumes, parser RFC 4180 do zero, BOM, encoding e benchmarks.
Regex para Validação de Formulários (2026): E-mail, CPF, Telefone, CEP
Coleção de regex prontas para campos brasileiros: e-mail, CPF, CNPJ, telefone, CEP, URL, data, senha forte, cartão. Inclui máscara em tempo real, integração React e ReDoS.
Grupos de Captura em Regex (2026): Named Groups, Referências e Lookahead
Guia completo de grupos em regex: capturing, non-capturing, named groups, backreferences, lookahead, lookbehind, replace com substituição e exemplos prontos em JavaScript, Python e PHP.