Expressões Regulares
Uma expressão regular (regex) é uma linguagem especial para descrever padrões de texto. Uma linha de regex pode fazer o que dezenas de linhas de código manual levariam para corresponder e extrair texto. Está em toda parte na limpeza de dados, análise de log e validação de formulários.
1. O que é uma Expressão Regular
Uma expressão regular usa símbolos especiais para descrever "que tipo de string estou procurando."
import re
# Regex mais simples: corresponder diretamente uma string
pattern = r"hello"
text = "hello world"
result = re.search(pattern, text)
if result:
print("Encontrado!") # Encontrado!
print(result.group()) # hello
print(result.start()) # 0 (posição inicial)
print(result.end()) # 5 (posição final)
r (raw string). r"\n" é barra invertida + letra n, não uma nova linha. Regex usa barras invertidas extensivamente; sem r, você teria que escrever \\n, o que é doloroso.
2. Métodos Comuns
import re
text = "Meu email é alice@example.com, também bob@teste.com"
# search() — encontra a primeira correspondência
result = re.search(r"\w+@\w+\.\w+", text)
print(result.group()) # alice@example.com
# findall() — encontra todas as correspondências
emails = re.findall(r"\w+@\w+\.\w+", text)
print(emails) # ['alice@example.com', 'bob@teste.com']
# match() — corresponde a partir do início
print(re.match(r"Meu", text)) # Match (no início)
print(re.match(r"email", text)) # None (não no início)
# sub() — substituir
masked = re.sub(r"\w+@", "***@", text)
print(masked) # Meu email é *@example.com, também *@teste.com
3. Referência Rápida de Metacaracteres
| Metacaractere | Significado | Exemplo | Correspondências |
|---|---|---|---|
. |
Qualquer caractere (exceto nova linha) | h.t |
hat, hot, hit |
\d |
Dígito | \d{3} |
123, 456 |
\w |
Letra/dígito/sublinhado | \w+ |
hello, abc123 |
\s |
Espaço em branco | \s |
espaço, tab, nova linha |
* |
0 ou mais do anterior | ab*c |
ac, abc, abbc |
+ |
1 ou mais do anterior | ab+c |
abc, abbc (não ac) |
? |
0 ou 1 do anterior | colou?r |
color, colour |
{n} |
Exatamente n vezes | \d{4} |
2026, 1990 |
{n,m} |
n a m vezes | \d{2,4} |
23, 456, 2026 |
^ |
Início da string | ^Hello |
Hello... |
$ |
Fim da string | fim$ |
...fim |
[] |
Conjunto de caracteres | [aeiou] |
Qualquer vogal |
| ` | ` | OU | `gato |
Exemplo: Validação de Telefone (Dificuldade ⭐⭐)
import re
def is_valid_phone(phone):
"""Valida um número de telefone celular brasileiro (11 dígitos, começa com 1 ou 9)"""
pattern = r"^[1-9]\d{10}$"
return bool(re.match(pattern, phone))
phones = ["11999887766", "21987654321", "010-12345678", "1199988776a"]
for p in phones:
print(f"{p}: {'✅' if is_valid_phone(p) else '❌'}")
# Extrair todos os números de telefone do texto
text = "Contato: 11999887766, backup: 21987654321, fixo: 010-12345678"
phones = re.findall(r"[1-9]\d{10}", text)
print(f"Telefones encontrados: {phones}")
4. Captura de Grupos
Use parênteses () para dividir o conteúdo correspondido em múltiplas partes:
import re
# Extrair nome de usuário e domínio de um email
email = "alice@example.com"
pattern = r"(\w+)@(\w+\.\w+)"
result = re.search(pattern, email)
if result:
print(f"Correspondência completa: {result.group(0)}") # alice@example.com
print(f"Usuário: {result.group(1)}") # alice
print(f"Domínio: {result.group(2)}") # example.com
# Extrair informações de um log
log = "2026-06-23 15:30:45 ERRO Timeout de conexão com banco de dados"
pattern = r"(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) (\w+) (.+)"
result = re.search(pattern, log)
print(f"Data: {result.group(1)}") # 2026-06-23
print(f"Hora: {result.group(2)}") # 15:30:45
print(f"Nível: {result.group(3)}") # ERRO
print(f"Mensagem: {result.group(4)}") # Timeout de conexão com banco de dados
Exemplo: Analisador de URL (Dificuldade ⭐⭐⭐)
import re
def parse_url(url):
"""Analisa uma URL para extrair protocolo, domínio, caminho e parâmetros de consulta"""
pattern = r"(https?)://([^/]+)(/[^?]*)?(?:\?([^#]*))?"
result = re.search(pattern, url)
if not result:
return None
return {
"protocol": result.group(1),
"domain": result.group(2),
"path": result.group(3) or "/",
"query": result.group(4) or ""
}
urls = [
"https://www.example.com/path/page.html?id=123&name=test",
"http://localhost:8080/api/usuarios",
"https://google.com"
]
for url in urls:
info = parse_url(url)
if info:
print(f"\nURL: {url}")
for key, value in info.items():
print(f" {key}: {value}")
5. Matching Greedy vs Lazy
Por padrão, * e + são greedy — eles correspondem o máximo possível. Adicionar ? os torna lazy — eles correspondem o mínimo possível:
import re
text = "<h1>Título</h1><p>Parágrafo</p>"
# Modo greedy — corresponde o máximo possível
greedy = re.search(r"<.+>", text)
print(greedy.group()) # <h1>Título</h1><p>Parágrafo</p>
# Modo lazy — corresponde o mínimo possível (adicione ?)
lazy = re.search(r"<.+?>", text)
print(lazy.group()) # <h1>
.*? (lazy) vs .* (greedy): Ao trabalhar com conteúdo estruturado como HTML ou JSON, o modo lazy é mais comum. O modo greedy frequentemente "ultrapassa" para corresponder conteúdo que não deveria. Em caso de dúvida, comece com lazy.
Casos de Uso Comuns
- Validação de formulário: Valide formato de email, números de telefone, números de identidade.
- Extração de dados: Extraia padrões específicos de páginas web, logs e texto.
- Substituição de texto: Substitua em lote conteúdo que corresponda a um padrão (ex.: mascarar números de telefone).
- Realce de sintaxe: Editores usam regex para identificar palavras-chave e strings.
- Análise de log: Filtre e processe padrões específicos de arquivos de log massivos.
❓ Perguntas Frequentes
P: O que significa o prefixo
rem regex? R:rindica uma raw string. Em uma string normal,\né uma nova linha; em uma raw string,\né apenas barra invertida + letra n. Regex usa barras invertidas extensivamente (como\d,\w); semr, você teria que escrever\\d, o que é muito doloroso. P: Qual o desempenho das expressões regulares? R: Rápido o suficiente para a maioria dos casos. Mas padrões mal escritos (como quantificadores aninhados(.*)*) podem causar "catastrophic backtracking" — levando minutos para texto simples. Soluções: evite quantificadores aninhados, use modo lazy e usere.compile()para padrões usados com frequência. P: Regex complexo é difícil de ler. Alguma dica? R: ① Adicione comentários — o modore.VERBOSEpermite espaços e comentários em regex. ② Divida em vários padrões simples. ③ Use ferramentas online como regex101.com para depuração. Regex é o clássico "divertido de escrever, doloroso de ler" — não tente resolver tudo com um único regex.
📖 Resumo
re.search()encontra a primeira correspondência;findall()encontra todas;sub()substitui- Metacaracteres:
\ddígito,\wcaractere de palavra,\sespaço em branco,.qualquer caractere - Quantificadores:
*qualquer,+pelo menos 1,?0 ou 1,{n}exatamente n - Grupos
()extraem partes;group(1)obtém o primeiro grupo - Greedy corresponde o máximo possível; adicione
?para lazy (o mínimo possível) - Sempre prefixe regex com
rpara evitar inferno de barras invertidas
📝 Atividades
-
Básico (Dificuldade ⭐): Escreva uma função
is_valid_email(email)que usa regex para validar um endereço de email (contém@, não vazio em ambos os lados, tem sufixo de domínio). -
Intermediário (Dificuldade ⭐⭐): Escreva uma função
extract_numbers(text)que extrai todos os números (incluindo inteiros e decimais) de uma string e retorna sua soma. Por exemplo,"Preço é 19.99, frete 5, cupom -10"→ retorna 14.99. -
Desafio (Dificuldade ⭐⭐⭐): Escreva um "mecanismo de template simples." Dado o template
"Olá, {nome}! Seu pedido {pedido_id} foi enviado, chegando em {dias} dias."e um dicionário{"nome": "Alice", "pedido_id": "2024001", "dias": 3}, use regex para substituir todos os placeholders{variavel}por valores reais. Dica: Usere.sub()com uma função de callback.



