regex test debug developer-tools regexp

Testador de Regex: O Guia Essencial para Depuração de Expressões Regulares

Simplifique o desenvolvimento de suas expressões regulares com o nosso testador em tempo real. Inclui correspondência ao vivo e trechos comuns.

Introdução

Expressões regulares — comumente chamadas de regex ou regexp — são sequências de caracteres que definem um padrão de busca. Elas são uma das ferramentas mais poderosas no arsenal de qualquer desenvolvedor, permitindo pesquisar, validar, extrair e transformar texto com uma única expressão compacta.

Seja validando um endereço de e-mail em um formulário web, extraindo dados de logs, ou realizando uma complexa operação de busca e substituição em milhares de arquivos, as expressões regulares permitem que você expresse exatamente o que procura em uma notação concisa e portátil que praticamente todas as linguagens de programação e a maioria dos editores de texto entendem.


Breve História das Expressões Regulares

A história das expressões regulares remonta aos fundamentos da ciência da computação teórica:

  • 1951 — O matemático Stephen Kleene formaliza o conceito de linguagens regulares e introduz a notação de estrela de Kleene (*) como parte de seu trabalho em teoria de autômatos.
  • 1968Ken Thompson implementa expressões regulares no editor de texto QED e posteriormente em ferramentas Unix como grep, sed e awk.
  • 1986POSIX padroniza duas variantes: BRE (Basic Regular Expressions) e ERE (Extended Regular Expressions).
  • 1997Philip Hazel cria a biblioteca PCRE (Perl Compatible Regular Expressions), que se torna o padrão de fato para regex avançado.
  • 1999ECMAScript 3 padroniza o objeto RegExp do JavaScript.
  • 2015ES6 adiciona as flags u (Unicode) e y (sticky).
  • 2018ES2018 adiciona grupos de captura nomeados ((?<nome>...)) e asserções lookbehind.

POSIX vs PCRE vs JavaScript RegExp

Recurso BRE/ERE (POSIX) PCRE JavaScript RegExp
Lookahead
Lookbehind ✓ (ES2018+)
Grupos nomeados ✓ (ES2018+)
Correspondência não-gulosa
Unicode Limitado ✓ (com flag u)
Retroreferências

Referência de Sintaxe Principal

Tabela de Referência Rápida

Padrão Significado
. Qualquer caractere exceto quebra de linha
^ Início da string / início de linha (com flag m)
$ Fim da string / fim de linha (com flag m)
\d Qualquer dígito [0-9]
\D Qualquer não-dígito
\w Caractere de palavra [a-zA-Z0-9_]
\W Caractere de não-palavra
\s Espaço em branco (espaço, tabulação, quebra de linha…)
\S Caractere que não é espaço em branco
[abc] Classe de caracteres — corresponde a a, b ou c
[^abc] Classe negada — corresponde a tudo exceto a, b, c
[a-z] Intervalo de caracteres
* 0 ou mais vezes (guloso)
+ 1 ou mais vezes (guloso)
? 0 ou 1 vez (guloso)
{n} Exatamente n vezes
{n,m} Entre n e m vezes (guloso)
*? +? ?? Equivalentes preguiçosos (não-gulosos)
(abc) Grupo de captura
(?:abc) Grupo de não-captura
(?<nome>abc) Grupo de captura nomeado
| Alternância — corresponde à esquerda OU direita
(?=...) Lookahead positivo
(?!...) Lookahead negativo
(?<=...) Lookbehind positivo
(?<!...) Lookbehind negativo

Classes de Caracteres

Classes de caracteres permitem corresponder a um caractere de um conjunto. [aeiou] corresponde a qualquer vogal. [a-zA-Z] corresponde a qualquer letra. [^0-9] corresponde a qualquer caractere que não seja um dígito.

Classes abreviadas comuns:

  • \d equivale a [0-9]
  • \w equivale a [a-zA-Z0-9_]
  • \s corresponde a espaço, tabulação (\t), quebra de linha (\n), retorno de carro (\r) e outros espaços em branco

Quantificadores: Gulosos vs Preguiçosos

Por padrão, os quantificadores são gulosos — correspondem ao máximo possível. Considere a string HTML <b>negrito</b> e <i>itálico</i>:

<.*>    → corresponde à string inteira de <b> a </i> (guloso)
<.*?>   → corresponde a <b>, depois </b>, depois <i>, depois </i> (preguiçoso)

Adicionar ? após um quantificador o torna preguiçoso (não-guloso): ele corresponde ao mínimo possível enquanto o padrão geral ainda for bem-sucedido.

Âncoras

  • ^ corresponde ao início da string (ou início de linha com a flag m).
  • $ corresponde ao fim da string (ou fim de linha com m).
  • \b corresponde a um limite de palavra — a transição entre um caractere de palavra e um que não é.
  • \B corresponde a um não-limite de palavra.

Grupos e Retroreferências

Grupos de captura (...) capturam o texto correspondente, ao qual você pode se referir depois como \1, \2, etc. (retroreferências), ou acessar através do array de resultados.

Grupos de não-captura (?:...) agrupam subpadrões sem criar uma captura, o que é mais eficiente quando você não precisa do valor capturado.

Grupos nomeados (?<ano>\d{4}) permitem referenciar capturas por nome (match.groups.ano em JavaScript), tornando os padrões muito mais legíveis.

Lookahead e Lookbehind

Essas asserções de largura zero correspondem a uma posição, não a um caractere:

\d+(?= reais)     → corresponde a dígitos somente se seguidos de " reais"
\d+(?! reais)     → corresponde a dígitos que NÃO são seguidos de " reais"
(?<=R\$)\d+       → corresponde a dígitos somente se precedidos por "R$"
(?<!R\$)\d+       → corresponde a dígitos que NÃO são precedidos por "R$"

Lookaheads e lookbehinds não consomem caracteres, portanto o texto correspondente não inclui a parte do lookahead/lookbehind.


Flags

Flag Nome Efeito
i Sem distinção de maiúsculas [a-z] também corresponde a [A-Z]
g Global Encontra todas as correspondências, não apenas a primeira
m Multilinha ^ e $ correspondem a limites de linha
s dotAll . também corresponde a quebras de linha
u Unicode Habilita correspondência Unicode completa; necessário para \p{}
y Sticky Corresponde apenas na posição lastIndex
x Verboso/Estendido Permite espaços em branco e comentários (apenas PCRE/Python)

A flag x (verbosa) é especialmente valiosa para documentar padrões complexos:

import re
pattern = re.compile(r"""
    ^                        # início da string
    (?P<year>\d{4})          # ano com 4 dígitos
    -
    (?P<month>0[1-9]|1[0-2]) # mês 01–12
    -
    (?P<day>0[1-9]|[12]\d|3[01])  # dia 01–31
    $
""", re.VERBOSE)

Padrões Comuns com Exemplos Reais

Validação de E-mail

^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$

Detalhamento:

  • ^[a-zA-Z0-9._%+-]+ — parte local (letras, dígitos e caracteres especiais selecionados)
  • @ — arroba literal
  • [a-zA-Z0-9.-]+ — nome do domínio
  • \.[a-zA-Z]{2,}$ — TLD com 2+ letras

Correspondência de URL

https?://(?:www\.)?[a-zA-Z0-9-]+(?:\.[a-zA-Z]{2,})+(?:/[^\s]*)?

Data ISO (YYYY-MM-DD)

\b\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])\b

Valida mês 0112 e dia 0131. Observe que não valida intervalos de dias específicos por mês (ex. 30 de fevereiro passaria na validação).

Endereço IPv4

\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b

Detalhamento:

  • 25[0-5] — 250–255
  • 2[0-4]\d — 200–249
  • [01]?\d\d? — 0–199

Número de Telefone (EUA)

\+?1?\s?[\(]?\d{3}[\)]?[-.\s]?\d{3}[-.\s]?\d{4}

Corresponde a formatos como (555) 123-4567, 555-123-4567, +1 555 123 4567.

Código de Cor Hexadecimal

#(?:[0-9A-Fa-f]{3}){1,2}\b

Corresponde a cores hex de 3 dígitos (#F00) e 6 dígitos (#FF0000).


Expressões Regulares em Diferentes Linguagens de Programação

JavaScript

// Sintaxe literal com flags
const regex = /^hello\s+world$/im;
const match = "Hello World".match(regex);

// Sintaxe construtora (útil para padrões dinâmicos)
const term = "world";
const dynamic = new RegExp(`hello\\s+${term}`, "im");

// Substituir todas as ocorrências
const result = "foo bar foo".replaceAll(/foo/g, "baz");

// Capturas nomeadas (ES2018+)
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const { year, month, day } = "2024-03-15".match(dateRegex).groups;

Python

import re

# Compilar para reutilização
pattern = re.compile(r'^hello\s+world$', re.IGNORECASE | re.MULTILINE)
match = pattern.match("Hello World")

# Encontrar todas as correspondências
dates = re.findall(r'\d{4}-\d{2}-\d{2}', text)

# Substituir com uma função
result = re.sub(r'\b\d+\b', lambda m: str(int(m.group()) * 2), "1 mais 2 é 3")

# Grupos nomeados
m = re.search(r'(?P<year>\d{4})-(?P<month>\d{2})', "2024-03")
print(m.group('year'))  # 2024

Java

import java.util.regex.*;

Pattern p = Pattern.compile("^hello\\s+world$",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
Matcher m = p.matcher("Hello World");
boolean found = m.matches();

// Extrair grupos
Pattern datePattern = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher dm = datePattern.matcher("Hoje é 2024-03-15");
if (dm.find()) {
    String year = dm.group(1);
}

Go

import "regexp"

re := regexp.MustCompile(`(?im)^hello\s+world$`)
match := re.FindString("Hello World")

// Encontrar todas as sub-correspondências
dateRe := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`)
all := dateRe.FindAllStringSubmatch(text, -1)
for _, m := range all {
    year, month, day := m[1], m[2], m[3]
    _ = year; _ = month; _ = day
}

// Grupos nomeados
namedRe := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})`)
match2 := namedRe.FindStringSubmatch("2024-03")
yearIdx := namedRe.SubexpIndex("year")
fmt.Println(match2[yearIdx]) // 2024

Performance e Backtracking Catastrófico

Como o Backtracking Funciona

A maioria dos motores de regex usa correspondência baseada em NFA (Autômato Finito Não Determinístico), o que significa que eles podem tentar múltiplos caminhos através do padrão quando uma tentativa anterior falha. Esse backtracking é o que possibilita recursos como lookaheads e retroreferências — mas também pode ser uma armadilha de performance.

O Caso Catastrófico

Considere o padrão (a+)+ aplicado à string "aaaaaX":

  1. O + externo tenta corresponder ao máximo de grupos possível.
  2. Quando o motor chega ao X e falha, ele faz backtracking e tenta diferentes maneiras de dividir os caracteres a entre as repetições do grupo.
  3. Para uma string de comprimento n, há 2^(n-1) divisões possíveis — levando à complexidade de tempo exponencial.
(a+)+  em "aaaaaaaaaaaaaaaaaX"  →  pode levar segundos ou minutos!

Outros padrões perigosos incluem (a|aa)+, (\w+\s*)+, e qualquer coisa com quantificadores aninhados sobre classes de caracteres sobrepostas.

Como Evitar

  1. Evitar quantificadores aninhados sobre o mesmo conjunto de caracteres: (a+)+ → use a+ em vez disso.
  2. Usar grupos atômicos (?>...) ou quantificadores possessivos a++ (PCRE) para evitar backtracking em grupos já correspondidos.
  3. Ser específico: substituir .* por uma classe de caracteres que exclua delimitadores (ex. [^"]* dentro de strings entre aspas).
  4. Ancorar padrões onde possível para que o motor falhe rapidamente.
  5. Usar timeout em código de produção ao processar entradas não confiáveis.

Como Ler uma Expressão Regular Complexa

Vamos decompor ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$:

^                    → âncora: início da string
[a-zA-Z0-9._%+-]+   → um ou mais caracteres permitidos na parte local
@                    → @ literal
[a-zA-Z0-9.-]+      → um ou mais caracteres de domínio
\.                   → ponto literal (escapado)
[a-zA-Z]{2,}        → TLD: 2 ou mais letras
$                    → âncora: fim da string

Dica: Use o testador de regex deste site para destacar cada grupo capturado e ver exatamente qual parte da entrada corresponde a cada token. Construa seu padrão incrementalmente — comece com a peça válida mais simples, verifique-a e então expanda.


Melhores Práticas

  1. Compilar uma vez, usar muitas vezes. Pré-compilar um padrão (ex. re.compile() no Python, Pattern.compile() no Java) é muito mais eficiente do que re-parseá-lo a cada chamada.
  2. Preferir grupos de não-captura (?:...) quando o valor capturado não é necessário. Isso sinaliza a intenção e evita alocação desnecessária de memória.
  3. Usar raw strings para padrões. No Python, use r'\d+' em vez de '\\d+' para evitar escape duplo. No JavaScript, a sintaxe literal /\d+/ lida com isso automaticamente.
  4. Nomear suas capturas. (?<ano>\d{4}) é muito mais manutenível do que depender do índice de grupo \1.
  5. Testar com casos extremos: strings vazias, strings que quase-mas-não-totalmente correspondem, caracteres Unicode e entradas muito longas.
  6. Documentar padrões complexos com a flag x (verbosa) no Python/PCRE, ou com comentários inline no seu código.
  7. Nunca usar regex para fazer parsing completo de HTML ou XML. Use uma biblioteca de parser adequada.
  8. Validar entradas no lado do servidor. A validação regex no lado do cliente melhora a UX, mas não deve ser a única linha de defesa.

Perguntas Frequentes (FAQ)

P: Qual é a diferença entre match() e search() no Python?
R: re.match() só corresponde no início da string. re.search() varre toda a string em busca de uma correspondência. Use re.fullmatch() para exigir que o padrão corresponda à string inteira.

P: Por que ^ dentro de uma classe de caracteres [^abc] significa algo diferente?
R: Dentro de uma classe de caracteres, ^ como primeiro caractere nega a classe — corresponde a qualquer caractere que não esteja no conjunto. Fora de uma classe de caracteres, ^ é uma âncora para o início da string.

P: Posso usar regex para fazer parsing de HTML?
R: Para extrações simples e bem definidas de estruturas HTML conhecidas, regex pode funcionar. Mas HTML não é uma linguagem regular — permite aninhamento arbitrário e tags de fechamento opcionais. Use um parser HTML adequado (ex. BeautifulSoup no Python, DOMParser no JS) para parsing robusto.

P: Qual é a diferença entre quantificadores gulosos e possessivos?
R: Quantificadores gulosos fazem backtracking — eles tentam a correspondência máxima e devolvem caracteres se necessário. Quantificadores possessivos (ex. a++ no PCRE) nunca devolvem — uma vez que correspondem, a correspondência fica bloqueada. Isso previne o backtracking catastrófico, mas também pode fazer com que uma correspondência falhe quando um quantificador guloso teria tido sucesso.

P: Como faço para corresponder a um ponto literal . ou parêntese (?
R: Escape-os com uma barra invertida: \. corresponde a um ponto literal, \( corresponde a um parêntese aberto literal.

P: Regex é sensível a maiúsculas por padrão?
R: Sim. Use a flag i (/padrão/i no JavaScript, re.IGNORECASE no Python) para habilitar a correspondência insensível a maiúsculas.

P: O que \b corresponde?
R: \b é uma asserção de limite de palavra de largura zero. Ela corresponde à posição entre um caractere de palavra (\w) e um caractere de não-palavra (\W).

P: Como testo se uma string inteira corresponde a um padrão?
R: Ancore com ^ e $: ^padrão$. No Python, você também pode usar re.fullmatch(). No JavaScript, use .test() com âncoras ou verifique que match()[0].length === input.length.


Use o testador de regex deste site para experimentar com cada padrão neste guia. Cole qualquer padrão, digite sua string de teste e veja as correspondências destacadas em tempo real.