Python RegEx
Aprenda expressões regulares em Python: sintaxe, sequências especiais, grupos, lookaheads, flags e funções do módulo re com exemplos claros.
Expressões regulares (regex) permitem pesquisar, extrair e substituir texto com base em padrões flexíveis em vez de strings exatas. O módulo re integrado do Python fornece tudo o que você precisa. Este capítulo cobre o conjunto completo de ferramentas de regex: sintaxe, sequências especiais, quantificadores, grupos, lookahead/lookbehind, flags e todas as funções essenciais do re — com exemplos corretos e executáveis ao longo do texto.
Por que Usar Expressões Regulares?
Os métodos de string comuns (str.find(), str.replace(), str.split()) funcionam bem para textos fixos. As expressões regulares se destacam quando o padrão varia:
- Validar se uma string parece um endereço de e-mail ou número de telefone.
- Extrair todas as datas de um documento, independentemente do formato exato.
- Remover tags HTML de uma string.
- Substituir múltiplas sequências de espaços em branco diferentes por um único espaço.
Quando a tarefa envolve descrever uma forma em vez de um valor fixo, use re.
Raw Strings
Os padrões de regex são quase sempre escritos como raw strings (r'...'). As raw strings tratam barras invertidas como caracteres literais, o que importa porque o regex usa muitas sequências com barra invertida (\d, \w, \s, \b). Sem o prefixo r, você precisaria de barras invertidas duplas em todo lugar:
import re
# Both patterns are identical — raw string is easier to read
re.findall(r'\d+', 'abc 123') # raw: r'\d+'
re.findall('\\d+', 'abc 123') # normal: '\\d+'Use raw strings para todo padrão de regex — é a convenção universal.
Sintaxe Básica: Metacaracteres
Metacaracteres são caracteres com significado especial dentro de um padrão. Caracteres literais correspondem exatamente a si mesmos.
| Metacaractere | Significado |
|---|---|
. | Qualquer caractere exceto nova linha |
^ | Início da string (ou de linha no modo MULTILINE) |
$ | Fim da string (ou de linha no modo MULTILINE) |
* | Zero ou mais do elemento anterior |
+ | Um ou mais do elemento anterior |
? | Zero ou um do elemento anterior |
{n} | Exatamente n repetições |
{n,m} | Entre n e m repetições |
[...] | Classe de caractere — qualquer caractere listado dentro |
[^...] | Classe negada — qualquer caractere não listado |
| | Alternância — a expressão da esquerda ou da direita |
() | Grupo de captura |
\ | Escapar um metacaractere ou iniciar uma sequência especial |
Para corresponder a um metacaractere literal como . ou *, escape-o com barra invertida: \. corresponde a um ponto real.
Sequências Especiais
Sequências especiais são classes de caracteres abreviadas que aparecem frequentemente em padrões do mundo real.
| Sequência | Corresponde a |
|---|---|
\d | Qualquer dígito — equivalente a [0-9] |
\D | Qualquer não-dígito |
\w | Caractere de palavra: letras, dígitos, sublinhado |
\W | Caractere que não é de palavra |
\s | Espaço em branco: espaço, tabulação, nova linha |
\S | Não-espaço em branco |
\b | Limite de palavra (largura zero) |
\B | Não-limite |
import re
print(re.findall(r'\d+', 'I have 3 cats and 12 dogs'))
# ['3', '12']
print(re.findall(r'\w+', 'hello_world 123'))
# ['hello_world', '123']
print(re.findall(r'\bPython\b', 'Python Pythonista Python3'))
# ['Python'] — word boundary prevents partial matches\b é particularmente útil: corresponde à posição entre um caractere de palavra e um não-caractere de palavra, portanto \bPython\b corresponde à palavra isolada "Python", mas não a "Pythonista" ou "Python3".
Quantificadores
Quantificadores controlam quantas vezes o elemento anterior deve corresponder.
import re
print(re.findall(r'a*', 'baaa')) # ['', 'aaa', '']
print(re.findall(r'a+', 'baaa')) # ['aaa']
print(re.findall(r'a?', 'baaa')) # ['', 'a', 'a', 'a', '']
print(re.findall(r'a{3}', 'baaa')) # ['aaa']Correspondência Gananciosa vs. Lazy
Por padrão, os quantificadores são gananciosos — eles correspondem ao máximo de texto possível. Adicione ? após o quantificador para torná-lo lazy (corresponder o mínimo possível).
import re
html = '<b>bold</b> and <i>italic</i>'
print(re.findall(r'<.*>', html))
# ['<b>bold</b> and <i>italic</i>'] — greedy: matches from first < to last >
print(re.findall(r'<.*?>', html))
# ['<b>', '</b>', '<i>', '</i>'] — lazy: matches each individual tagQuantificadores lazy são essenciais ao analisar texto estruturado como HTML ou fragmentos JSON.
Classes de Caracteres
Uma classe de caractere [...] corresponde a qualquer caractere do conjunto listado. Use - para intervalos e ^ no início para negar a classe.
import re
print(re.findall(r'[aeiou]', 'hello world'))
# ['e', 'o', 'o']
print(re.findall(r'[0-9]', 'a1b2c3'))
# ['1', '2', '3']
print(re.findall(r'[^aeiou\s]+', 'hello world'))
# ['h', 'll', 'w', 'rld'] — consonants only (not vowels, not spaces)Intervalos comuns prontos para uso: [a-z] letras minúsculas, [A-Z] maiúsculas, [0-9] dígitos, [a-zA-Z0-9] alfanumérico.
Âncoras
Âncoras não consomem caracteres — elas afirmam uma posição na string.
import re
print(re.findall(r'^Python', 'Python is great'))
# ['Python'] — matches only if 'Python' is at the start
print(re.findall(r'great$', 'Python is great'))
# ['great'] — matches only if 'great' is at the end
print(re.findall(r'^Python', 'Learn Python'))
# [] — 'Python' is not at the start of this stringConsulte a flag re.MULTILINE mais adiante neste capítulo para aplicar ^ e $ por linha em vez de por string.
Alternância
O pipe | funciona como um OR lógico entre duas expressões.
import re
print(re.findall(r'cat|dog', 'I have a cat and a dog'))
# ['cat', 'dog']
print(re.findall(r'colou?r|colour', 'color and colour'))
# ['color', 'colour']Grupos de Captura
Parênteses () criam um grupo de captura. Os grupos permitem extrair partes de uma correspondência. re.search() retorna um objeto de correspondência; chame .group(n) ou .groups() nele.
import re
match = re.search(r'(\d{4})-(\d{2})-(\d{2})', '2023-10-05')
if match:
print(match.group(0)) # '2023-10-05' — entire match
print(match.group(1)) # '2023'
print(match.groups()) # ('2023', '10', '05')Grupos Nomeados
Nomeie um grupo com (?P<name>...) para acessá-lo pelo nome em vez da posição. Isso torna os padrões muito mais fáceis de manter.
import re
match = re.search(
r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})',
'2023-10-05'
)
if match:
print(match.group('year')) # '2023'
print(match.groupdict()) # {'year': '2023', 'month': '10', 'day': '05'}Grupos Não-Capturadores
Use (?:...) quando precisar agrupar, mas não precisar capturar o valor.
import re
print(re.findall(r'(?:Mr|Mrs|Ms)\.? \w+', 'Mr. Smith and Mrs. Jones'))
# ['Mr. Smith', 'Mrs. Jones']Lookahead e Lookbehind
Lookahead ((?=...)) e lookbehind ((?<=...)) afirmam que algo segue ou precede a correspondência, sem incluí-lo no resultado. Eles têm largura zero: não consomem caracteres.
import re
# Positive lookahead — find numbers followed by 'dollars'
print(re.findall(r'\d+(?= dollars)', '100 dollars and 200 euros'))
# ['100']
# Negative lookahead — find numbers NOT followed by 'dollars'
print(re.findall(r'\b\d+\b(?! dollars)', '100 dollars and 200 euros'))
# ['200']
# Positive lookbehind — extract domain from email addresses
emails = 'Contact [email protected] or [email protected]'
print(re.findall(r'(?<=@)\w+\.\w+', emails))
# ['example.com', 'test.org']Os padrões lookbehind devem ter largura fixa em Python — não é possível usar * ou + dentro deles.
O Módulo re: Funções Principais
re.search() — Encontrar a Primeira Correspondência
Retorna um objeto de correspondência para o primeiro local onde o padrão corresponde, ou None se não houver correspondência.
import re
text = 'apple banana apple'
match = re.search(r'banana', text)
if match:
print(match.group()) # 'banana'
print(match.span()) # (6, 12)re.match() — Corresponder Apenas no Início
Como re.search(), mas o padrão deve corresponder no início da string.
import re
print(bool(re.match(r'\d+', 'abc123'))) # False — no digit at start
print(bool(re.search(r'\d+', 'abc123'))) # True — digit found anywhereUse re.search() quando quiser encontrar um padrão em qualquer lugar; use re.match() quando o padrão deva aparecer no início.
re.fullmatch() — Corresponder a String Inteira
O padrão deve corresponder à string inteira do início ao fim.
import re
print(bool(re.fullmatch(r'\d{5}', '12345'))) # True — exactly 5 digits
print(bool(re.fullmatch(r'\d{5}', '123456'))) # False — too long
print(bool(re.fullmatch(r'\d{5}', '1234X'))) # False — non-digit presentre.fullmatch() é ideal para validação de entrada (CEPs, números de telefone, etc.).
re.findall() — Todas as Correspondências Não Sobrepostas
Retorna uma lista de todas as correspondências. Se o padrão tiver grupos, retorna uma lista de tuplas.
import re
print(re.findall(r'\d+', 'abc 123 def 456'))
# ['123', '456']
# With a group — returns list of group values
print(re.findall(r'(\w+)@(\w+\.\w+)', '[email protected] [email protected]'))
# [('alice', 'example.com'), ('bob', 'test.org')]re.finditer() — Iterador de Objetos de Correspondência
Como re.findall(), mas produz objetos de correspondência um de cada vez. Útil para textos grandes ou quando você precisa de informações de posição.
import re
for m in re.finditer(r'\d+', 'abc 123 def 456'):
print(m.group(), m.start(), m.end())
# 123 4 7
# 456 12 15re.sub() — Substituir Correspondências
Substitui cada ocorrência do padrão por uma string de substituição ou pelo valor de retorno de uma função.
import re
text = 'apple banana apple'
print(re.sub(r'apple', 'orange', text))
# 'orange banana orange'
# Limit replacements
print(re.sub(r'apple', 'orange', text, count=1))
# 'orange banana apple'
# Backreferences in replacement — reformat a date
print(re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3/\2/\1', '2023-10-05'))
# '05/10/2023're.split() — Dividir por um Padrão
Divide a string em cada ocorrência do padrão.
import re
print(re.split(r',\s*', 'apple, banana, cherry, date'))
# ['apple', 'banana', 'cherry', 'date']
# Limit the number of splits
print(re.split(r',\s*', 'apple, banana, cherry, date', maxsplit=2))
# ['apple', 'banana', 'cherry, date']Compilando Padrões com re.compile()
Quando você usa o mesmo padrão muitas vezes, compile-o uma vez com re.compile() para melhorar o desempenho. O objeto de padrão resultante tem todos os mesmos métodos (findall, search, sub, etc.).
import re
# Compile once
digit_pattern = re.compile(r'\d+')
# Reuse many times
print(digit_pattern.findall('abc 123 def 456')) # ['123', '456']
print(digit_pattern.sub('NUM', 'abc 123 def 456')) # 'abc NUM def NUM'
print(digit_pattern.search('xyz 99')) # <re.Match object ...>A compilação é especialmente valiosa dentro de laços ou funções chamadas com frequência.
Flags
As flags modificam o comportamento da correspondência. Passe-as como último argumento para qualquer função re, ou inclua-as ao chamar re.compile(). Múltiplas flags podem ser combinadas com |.
| Flag | Forma curta | Efeito |
|---|---|---|
re.IGNORECASE | re.I | Correspondência sem distinção entre maiúsculas e minúsculas |
re.MULTILINE | re.M | ^ e $ correspondem ao início/fim de cada linha |
re.DOTALL | re.S | . também corresponde a novas linhas |
re.VERBOSE | re.X | Permite espaços em branco e comentários dentro do padrão |
import re
# IGNORECASE
print(re.findall(r'hello', 'Hello HELLO hello', re.IGNORECASE))
# ['Hello', 'HELLO', 'hello']
# MULTILINE — ^ matches the start of each line
text = 'first line\nsecond line\nthird line'
print(re.findall(r'^\w+', text, re.MULTILINE))
# ['first', 'second', 'third']
# DOTALL — . matches newline characters
print(re.findall(r'<.*?>', '<div>\n<p>text</p>\n</div>', re.DOTALL))
# ['<div>', '<p>', '</p>', '</div>']
# VERBOSE — write readable patterns with comments
date_pattern = re.compile(r'''
(?P<year>\d{4}) # four-digit year
-
(?P<month>\d{2}) # two-digit month
-
(?P<day>\d{2}) # two-digit day
''', re.VERBOSE)
print(date_pattern.search('2023-10-05').groupdict())
# {'year': '2023', 'month': '10', 'day': '05'}Escapando Entrada do Usuário com re.escape()
Se você construir um padrão a partir de texto fornecido pelo usuário, sempre o escape primeiro para evitar interpretações não intencionais de metacaracteres.
import re
user_input = 'hello.world'
# Without escaping, '.' matches any character
# With escaping, '\.' matches a literal dot
safe_pattern = re.escape(user_input)
print(safe_pattern) # 'hello\\.world'
print(bool(re.search(safe_pattern, 'say hello.world'))) # True
print(bool(re.search(safe_pattern, 'say helloXworld'))) # FalseExemplos Práticos
Validar um Endereço de E-mail
import re
def is_valid_email(email):
pattern = r'^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$'
return bool(re.fullmatch(pattern, email))
print(is_valid_email('[email protected]')) # True
print(is_valid_email('bad-email@')) # FalseExtrair Todas as URLs do Texto
import re
text = 'Visit https://www.example.com or http://test.org for details.'
urls = re.findall(r'https?://[\w./-]+', text)
print(urls)
# ['https://www.example.com', 'http://test.org']Remover Espaços em Branco Extras
import re
messy = ' hello world Python '
clean = re.sub(r'\s+', ' ', messy).strip()
print(clean) # 'hello world Python'Analisar uma Linha de Log
import re
log = '2023-10-05 14:32:11 ERROR Failed to connect to database'
pattern = re.compile(
r'(?P<date>\d{4}-\d{2}-\d{2}) '
r'(?P<time>\d{2}:\d{2}:\d{2}) '
r'(?P<level>\w+) '
r'(?P<message>.+)'
)
m = pattern.match(log)
if m:
print(m.group('level')) # 'ERROR'
print(m.group('message')) # 'Failed to connect to database'Armadilhas Comuns
re.match() vs re.search() — re.match() verifica apenas o início da string. Novos usuários frequentemente esperam que ele pesquise em qualquer lugar e ficam confusos quando retorna None.
Esquecer raw strings — '\d' em uma string Python normal é apenas 'd' com um escape não reconhecido (ou um SyntaxWarning no Python 3.12+). Sempre escreva r'\d'.
Correspondências gananciosas consumindo demais — Se um padrão .* capturar mais do que você pretendia, mude para .*? (lazy).
Caracteres especiais em strings de substituição — Em re.sub(), sequências com barra invertida como \1 na substituição referem-se a grupos capturados. Para incluir uma barra invertida literal na substituição, escreva \\.
re.findall() com grupos — Quando seu padrão contém grupos, re.findall() retorna os grupos, não a correspondência completa. Use um grupo não-capturador (?:...) se quiser a correspondência completa.
Referência Rápida
| Tarefa | Função |
|---|---|
| Encontrar primeira correspondência | re.search(pattern, text) |
| Encontrar todas as correspondências | re.findall(pattern, text) |
| Iterar correspondências | re.finditer(pattern, text) |
| Corresponder no início | re.match(pattern, text) |
| Corresponder string inteira | re.fullmatch(pattern, text) |
| Substituir | re.sub(pattern, repl, text) |
| Dividir | re.split(pattern, text) |
| Compilar para reutilização | re.compile(pattern) |
| Escapar entrada do usuário | re.escape(text) |
Capítulos Relacionados
- Python Strings — métodos de string que complementam o regex para tarefas de texto mais simples.
- Modify Strings — métodos integrados como
replace()esplit()que dispensam o regex quando os padrões são fixos. - Python File Handling — leitura de arquivos linha por linha para aplicar regex em escala.
- Python Try/Except — tratamento de erros que surgem quando padrões de regex são compilados a partir de entrada do usuário.
- Python Functions — encapsulamento da lógica de regex em funções reutilizáveis.