Expressões regulares: classes, quantificadores e grupos
Uma expressão regular, ou regex, é um pequeno padrão que descreve um texto: “quatro dígitos, um hífen, dois dígitos”, ou “uma palavra que começa com letra maiúscula”. Os programas usam regex para buscar, para conferir o que o usuário digitou e para extrair pedaços de uma linha. Este guia explica as peças de um padrão, com exemplos para colar no testador de regex, e depois as armadilhas: padrões que encontram mais do que deviam, padrões que podem não terminar nunca e as diferenças entre os motores de JavaScript, Python e PHP. Os exemplos usam a sintaxe do JavaScript, a mesma do testador e de todos os navegadores.
Caracteres que significam outra coisa
A maioria dos caracteres de um padrão representa a si mesma: sol encontra as letras s, o e l nessa ordem. Quatorze não, porque têm uma função no padrão: . ^ $ * + ? ( ) [ ] { } | \ e, dentro do código JavaScript, a barra que fecha o padrão. Para buscar um deles literalmente, coloque uma barra invertida antes.
1.5Encontra 1.5, mas também 1x5 e 1-5: o ponto representa qualquer caractere.1\.5Encontra só 1.5: a barra invertida faz do ponto um ponto.R\$ ?\d+Encontra R$ 30 e R$30: o cifrão escapado, um espaço opcional e dígitos.
Classes de caracteres
Uma classe encontra um caractere de um conjunto. Os colchetes listam o conjunto; uma barra invertida com uma letra dá um conjunto pronto; a letra maiúscula significa o contrário.
| Padrão | Encontra um… |
|---|---|
[aeiou] | vogal sem acento: a, e, i, o ou u |
[^aeiou] | caractere que não seja nenhum desses, incluindo espaços e dígitos |
[a-z0-9] | letra minúscula de a a z, ou um dígito |
\d | dígito de 0 a 9 (\D: qualquer outro) |
\w | letra de A a Z ou de a a z, dígito ou sublinhado (\W: qualquer outro) |
\s | espaço, tabulação, quebra de linha ou outro espaço em branco (\S: qualquer outro) |
. | caractere exceto quebra de linha (qualquer um com a flag s) |
\p{L} | letra de qualquer alfabeto, com a flag u ou v |
No JavaScript, \d e \w só conhecem o alfabeto latino básico: \w+ quebra São em S e o, e \d não encontra o dígito arábico oriental ٣. Com a flag u, \p{L}+ encontra São inteiro e \p{Nd} encontra dígitos de qualquer escrita. O Python 3 faz o contrário: em texto comum, o \d e o \w dele já cobrem Unicode, a não ser com a flag ASCII. O motor PCRE2 do PHP fica no ASCII enquanto as propriedades Unicode não forem ligadas, por exemplo com (*UCP) no começo do padrão.
Quantificadores: quantas vezes
Um quantificador diz quantas vezes o elemento antes dele pode se repetir. Ele vale para um caractere, uma classe ou um grupo entre parênteses.
| Padrão | Significa |
|---|---|
a* | zero ou mais |
a+ | um ou mais |
a? | zero ou um, ou seja, opcional |
a{3} | exatamente três |
a{3,} | três ou mais |
a{2,4} | de dois a quatro |
Os quantificadores são gulosos: pegam tudo o que podem e só devolvem o que o resto do padrão precisa. Em <b>negrito</b>, o padrão <.+> encontra a string inteira, do primeiro < ao último >. Um ponto de interrogação depois deixa o quantificador preguiçoso, que pega o mínimo: <.+?> encontra <b> e depois </b>. colou?r encontra as duas grafias inglesas, color e colour, e \d{3,} encontra números de três dígitos ou mais.
Âncoras e limites de palavra
Âncoras encontram uma posição, não um caractere. ^ é o começo do texto e $ o fim; com a flag m, viram o começo e o fim de cada linha. \b é um limite de palavra, o ponto entre um caractere de palavra e qualquer outra coisa, então \bsol\b encontra sol como palavra, mas não dentro de solar. Sem âncoras, um padrão usado para validar também aceita texto com caracteres a mais em volta da parte que bate: \d{4} encontra quatro dígitos dentro de abc12345, enquanto ^\d{4}$ só aceita exatamente quatro dígitos.
\bsol\bEm “sol solar sol.”: duas ocorrências, as duas palavras sol.^\d{4}$Aceita 2026; recusa 12026 e 2026a.
Grupos, alternativas e retrovisores
Os parênteses agrupam parte de um padrão para que um quantificador ou uma alternativa valha para o grupo todo, e capturam o texto encontrado para que um programa possa lê-lo: (\d{4})-(\d{2})-(\d{2}) captura ano, mês e dia separados. (?:…) agrupa sem capturar, e (?<ano>\d{4}) dá nome à captura.
A barra vertical significa “ou” e tem a menor precedência de todas: ^sol|mar$ significa “sol no começo” ou “mar no fim”, então encontra sol em solar e mar em amar. Para dizer “o texto inteiro é sol ou mar”, agrupe as alternativas: ^(?:sol|mar)$.
Um retrovisor encontra o mesmo texto que um grupo já capturou: (\w)\1 encontra uma letra seguida dela mesma, rr e ss em “carro e passarinho”, e \k<nome> faz o mesmo com um grupo nomeado.
Lookahead e lookbehind
Um lookaround confere o que vem antes ou depois de uma posição sem incluir isso no resultado. (?=…) exige que algo venha depois, (?!…) proíbe; (?<=…) exige que algo venha antes, (?<!…) proíbe. Servem para pegar um número pela unidade ou pela moeda, ou para juntar várias condições sobre o mesmo texto.
\d+(?=%)Em “20% de 500”: encontra 20, o número seguido de um sinal de porcentagem, sem o sinal.(?<=R\$ ?)\d+Em “R$ 25 e US$ 30”: encontra 25, o valor depois do símbolo do real.^(?=.*\d).{8,}$Aceita senhasecreta1 e recusa senhasecreta: pelo menos oito caracteres, um deles um dígito.
Flags
As flags mudam como o padrão inteiro é aplicado. No JavaScript, elas vêm depois da barra de fechamento, como em /sol/gi; no testador, elas têm um campo próprio.
| Flag | Efeito |
|---|---|
g | encontrar todas as ocorrências, não só a primeira |
i | ignorar a diferença entre maiúsculas e minúsculas |
m | ^ e $ valem para cada linha |
s | o ponto também encontra quebras de linha |
u | modo Unicode: \p{…}, e um emoji conta como um caractere |
v | modo Unicode com operações de conjunto nas classes; use no lugar de u |
y | grudenta (sticky): buscar só onde a ocorrência anterior terminou |
d | registrar onde cada grupo começa e termina |
Validar e-mails, datas e outros formatos
Uma regex consegue conferir a forma de um dado, não se ele é real. O padrão HTML define o que um <input type="email"> aceita com um padrão próprio, e diz abertamente que se afasta de propósito da RFC 5322, a norma do formato de e-mail, que permite formas como nomes entre aspas com espaços que quase ninguém usa. Ele aceita ana@example.com e também ana@localhost; recusa ana@my_domain.example, porque um nome de domínio não pode ter sublinhado. Se a caixa existe, só uma mensagem diz.
Com datas é parecido: ^\d{4}-\d{2}-\d{2}$ aceita 2026-02-31, que não existe. Confira a forma com o padrão e deixe uma função de datas decidir se a data é válida. E não analise HTML nem JSON com expressões regulares: estruturas aninhadas precisam de um parser.
Quando um padrão não termina nunca
A maioria dos motores, incluindo os de JavaScript, Python e PHP, funciona por retrocesso (backtracking): quando um caminho falha, volta e tenta outra forma de dividir o texto entre as partes do padrão. Um grupo que repete algo que também se repete, como ^(a+)+$, pode ser dividido num número de formas que dobra a cada caractere a mais. Numa sequência de letras a terminada em ponto de exclamação o padrão nunca pode bater, mas o motor testa todas as divisões antes de desistir. A OWASP chama isso de negação de serviço por expressões regulares, ReDoS, e cita padrões como (a+)+$, ([a-zA-Z]+)*$ e (a|aa)+$.
A solução é tirar a ambiguidade: ^a+$ descreve o mesmo texto e falha na hora. Evite quantificadores aninhados e alternativas que possam encontrar os mesmos caracteres, e nunca execute um padrão vindo de um usuário sem limite de tempo. O testador de regex roda cada busca num worker separado e a interrompe depois de meio segundo, então um padrão desses é avisado em vez de travar a página.
Nem todo motor é igual
O básico acima funciona em quase todo lugar, mas os detalhes mudam de uma linguagem para outra. Um padrão escrito para um motor deve ser testado no outro antes de você confiar nele.
| Recurso | JavaScript | Python (re) | PHP (PCRE2) |
|---|---|---|---|
| Grupo nomeado | (?<nome>…) | (?P<nome>…) | as duas formas |
| \d e \w | só ASCII | Unicode por padrão | ASCII, a não ser com UCP |
| Quantificadores possessivos e grupos atômicos (a++, (?>…)) | não | sim, desde o Python 3.11 | sim |
| Flags embutidas como (?i) | só a forma delimitada (?i:…), desde o ECMAScript 2025 | sim | sim |
O lookbehind também muda: o JavaScript aceita um de tamanho variável, como (?<=[a-z]+), enquanto o Python exige largura fixa e o recusa. O Python também escreve as barras invertidas de outro jeito no código: uma string crua, r"\d+", mantém as barras como foram digitadas, enquanto os padrões JavaScript entre barras não precisam dobrá-las. Numa string JavaScript passada para new RegExp(), cada barra invertida precisa ser escrita duas vezes.
Perguntas frequentes
Por que meu padrão só encontra uma ocorrência?
No JavaScript, a busca para na primeira ocorrência se a flag g não estiver ligada. No testador, acrescente g às flags.
Por que o \w não encontra letras acentuadas nem o ç?
Porque no JavaScript o \w significa só de A a Z, de a a z, dígitos e sublinhado. Use \p{L} com a flag u para letras de qualquer alfabeto.
Como encontro um ponto, um parêntese ou um ponto de interrogação?
Coloque uma barra invertida antes: \. \( \? e assim por diante. Dentro de colchetes a maioria perde o significado, então [.?] encontra um ponto ou um ponto de interrogação.
Uma expressão regular consegue validar um e-mail?
Só a forma dele. O padrão do HTML é uma boa escolha para formulários; se o endereço recebe e-mail, só dá para saber enviando uma mensagem.
Por que meu padrão travou o programa?
Provavelmente ele repete um grupo que também se repete, como (a+)+, e o texto quase bate. Reescreva sem quantificadores aninhados e rode padrões de usuários com limite de tempo.
Referências
- MDN Web Docs — Regular expressions (JavaScript guide)
- MDN Web Docs — Regular expression syntax cheat sheet
- ECMAScript® Language Specification — RegExp (Regular Expression) Objects
- TC39 — Finished proposals (RegExp modifiers and duplicate named groups: ECMAScript 2025)
- Python documentation — re: regular expression operations
- PCRE2 — pcre2pattern: syntax and semantics of the regular expressions
- OWASP — Regular expression Denial of Service (ReDoS)
- WHATWG HTML Standard — Valid email address
- RFC 5322 — Internet Message Format (October 2008)
Guias relacionados
- Endereços IP explicados: IPv4, IPv6 e as faixas reservadas
- Sub-redes e CIDR explicados: prefixos, máscaras e hosts utilizáveis
- Registros DNS explicados: A, AAAA, CNAME, MX, TXT e NS
- Um site está fora do ar ou é só com você? Como verificar, passo a passo
- Códigos de status HTTP e erros da Cloudflare: o que significam 403, 404, 429, 500, 503 e 521
- Timestamps Unix e fusos horários explicados
- Contagem de datas: dias entre datas, anos bissextos e semanas ISO
- Fusos horários e horário de verão: reuniões entre cidades
- Formatos de imagem explicados: JPEG, PNG, WebP, AVIF e HEIC
- O tamanho de uma imagem explicado: pixels, DPI, peso e qualidade
- Fotos de celular explicadas: HEIC, metadados EXIF e sua privacidade
- Força de senhas explicada: comprimento, entropia e frases-senha
- Funções de hash explicadas: SHA-256, checksums e hash de senhas
- JSON Web Tokens explicados: claims, assinaturas e validação segura
- Chaves de API explicadas: tamanho, formato, armazenamento e rotação
- UUIDs explicados: v4, v7 e como escolher a versão certa
- QR codes explicados: capacidade, correção de erros e tamanho de impressão
- Porcentagens e IVA explicados: somar, tirar e acumular
- Erros comuns em JSON e como corrigi-los
- Arquivos CSV explicados: separadores, aspas, codificação e Excel
- Codificações de texto explicadas: UTF-8, Base64 e codificação de URL
- Edição de PDFs no navegador: o que se mantém ao juntar, dividir e converter