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."

PYTHON
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)
⚠️ Sempre prefixe strings regex com 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

PYTHON
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 ⭐⭐)

PYTHON
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}")
▶ Experimente

4. Captura de Grupos

Use parênteses () para dividir o conteúdo correspondido em múltiplas partes:

PYTHON
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 ⭐⭐⭐)

PYTHON
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}")
▶ Experimente

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:

PYTHON
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


❓ Perguntas Frequentes

P: O que significa o prefixo r em regex? R: r indica 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); sem r, 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 use re.compile() para padrões usados com frequência. P: Regex complexo é difícil de ler. Alguma dica? R: ① Adicione comentários — o modo re.VERBOSE permite 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


📝 Atividades

  1. 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).

  2. 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.

  3. 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: Use re.sub() com uma função de callback.

100%