Expressões Regulares em Python: Correspondência e Substituição de Texto

Utilizando Expressões Regulares para Correspondência

O módulo re do Python é a ferramenta padrão para trabalhar com expressões regulares, permitindo operações sofisticadas de correspondência e manipulação de texto. O fluxo de trabalho comum envolve compilar um padrão regex em um objeto Pattern e, em seguida, usar métodos desse objeto para procurar ou corresponder no texto alvo.

Exemplo Básico de Correspondência

Considere o seguinte exemplo que demonstra o uso de re.search(), re.match() e re.findall():

import re

texto_alvo = "Dados: ID_98765, Valor: 123.45, Temperatura: -5°C. Outros números: 100, 2000, 3.14."
# Compila um padrão para encontrar sequências de dígitos
padrao_digitos = re.compile(r"\d+")

# Tenta encontrar o padrão em qualquer lugar do texto
resultado_busca = padrao_digitos.search(texto_alvo)
print(f"Resultado search: {resultado_busca}")

# Tenta encontrar o padrão apenas no início do texto
resultado_inicio = padrao_digitos.match(texto_alvo)
print(f"Resultado match: {resultado_inicio}")

# Encontra todas as ocorrências do padrão no texto
todos_numeros = padrao_digitos.findall(texto_alvo)
print(f"Todos os números encontrados: {todos_numeros}")

Neste código, re.compile(r"\d+") cria um objeto padrão que busca um ou mais dígitos. O prefixo r antes da string da expressão regular indica uma "raw string" (string bruta) em Python.

Strings Brutas (Raw Strings) e o Prefixo r

O prefixo r é essencial para evitar problemas com caracteres de escape. Em Python, a barra invertida (\) é um caractere de escape, assim como nas expressões regulares. Sem o r, o Python interpretaria a string primeiro, o que exigiria dobrar as barras invertidas para que o motor de regex recebesse a barra invertida pretendida.

Por exemplo, para corresponder a um único caractere de barra invertida (\), o regex exige \\. Em uma string Python normal, isso seria "\\\\" para que o Python a interprete como \\ e, em seguida, o regex a interprete como \. Com o prefixo r, você pode simplesmente escrevre r"\\".

String Regular Python String Bruta Python Interpretação Regex
"ab*" r"ab*" ab*
"\\w+\\s+\\1" r"\w+\s+\1" \w+\s+\1
"\\\\section" r"\\section" \section

Flags de Compilação

O método re.compile() pode aceitar um argumento flags para modificar o comportamento do padrão. Algumas flags comuns incluem:

Flag Significado
re.ASCII, re.A Faz com que \w, \b, \s e \d correspondam apenas a caracteres ASCII.
re.DOTALL, re.S Permite que o metacaractere . (ponto) corresponda a qualquer caractere, incluindo quebras de linha.
re.IGNORECASE, re.I Realiza uma correspondência que ignora a distinção entre maiúsculas e minúsculas.
re.MULTILINE, re.M Afeta os metacaracteres ^ e $, peermitindo que correspondam ao início/fim de cada linha, não apenas ao início/fim da string completa.
re.VERBOSE, re.X Permite que você escreva expressões regulares de forma mais legível, ignorando espaços em branco (exceto quando escapados ou dentro de classes de caracteres) e permitindo comentários.

A flag re.VERBOSE é particularmente útil para padrões complexos, tornando-os mais fáceis de entender e manter:

import re

# Padrão para identificar um endereço de e-mail básico usando VERBOSE
padrao_email = re.compile(r"""
    ^                  # Início da string
    [a-zA-Z0-9._%+-]+  # Parte do nome de usuário
    @                  # O símbolo de arroba
    [a-zA-Z0-9.-]+     # Parte do domínio
    \.                 # Um ponto literal
    [a-zA-Z]{2,6}      # Extensão do domínio (ex: com, org, net)
    $                  # Fim da string
""", re.VERBOSE)

email_teste = "exemplo.usuario@dominio.com.br"
if padrao_email.match(email_teste):
    print(f"'{email_teste}' é um e-mail válido.")
else:
    print(f"'{email_teste}' NÃO é um e-mail válido.")

Métodos de Correspondência de Padrões

Após a compilação de um objeto padrão, vários métodos podem ser usados para realizar correspondências:

Método Propósito
match() Verifica se o padrão corresponde ao início da string. Retorna um objeto de correspondência ou None.
search() Varre a string em busca da primeira localização onde o padrão corresponde. Retorna um objeto de correspondência ou None.
findall() Retorna uma lista de todas as substrings não sobrepostas que correspondem ao padrão.
finditer() Retorna um iterador que produz objetos de correspondência para todas as ocorrências não sobrepostas.

Objetos de Correspondência (Match Objects)

Quando match() ou search() encontram uma correspondência, eles retornam um objeto de correspondência, que contém informações detalhadas sobre a correspondência. Métodos importantes de objetos de correspondência incluem:

Método Propósito
group([grupo1, ...]) Retorna a(s) substring(s) que corresponde(m) a um grupo ou todos os grupos se nenhum argumento for fornecido. O grupo 0 representa a correspondência completa.
start([grupo]) Retorna o índice inicial da substring correspondente para um grupo específico (ou a correspondência completa).
end([grupo]) Retorna o índice final (exclusivo) da substring correspondente para um grupo específico (ou a correspondência completa).
span([grupo]) Retorna uma tupla (start, end) para um grupo específico (ou a correspondência completa).

Exemplo de uso de grupos em um objeto de correspondência:

import re

# Padrão para extrair ano, mês e dia de uma data
padrao_data = re.compile(r"(\d{4})-(\d{2})-(\d{2})")
texto_com_data = "Hoje é 2023-10-27, amanhã será 2023-10-28."
correspondencia = padrao_data.search(texto_com_data)

if correspondencia:
    print(f"Texto completo da correspondência (grupo 0): '{correspondencia.group(0)}'")
    print(f"Ano (grupo 1): '{correspondencia.group(1)}'")
    print(f"Mês (grupo 2): '{correspondencia.group(2)}'")
    print(f"Dia (grupo 3): '{correspondencia.group(3)}'")
    print(f"Posição da correspondência (span): {correspondencia.span()}")
else:
    print("Nenhuma data encontrada.")

Modificando Strings com Expressões Regulares

O módulo re também oferece funções poderosas para dividir e substituir partes de strings com base em padrões regex.

Dividindo Strings com re.split()

O método split(string[, maxsplit=0]) divide uma string em uma lista, usando o padrão regex como delimitador. O parâmetro opcional maxsplit limita o número de divisões.

import re

# Divide a string usando vírgulas (com espaços opcionais ao redor) como delimitador
separador_personalizado = re.compile(r",\s*")
frase = "maçã, banana,laranja, uva"

partes = separador_personalizado.split(frase)
print(f"Divisão padrão: {partes}")

# Divide a string, limitando o número de divisões a 2
partes_limitadas = separador_personalizado.split(frase, 2)
print(f"Divisão limitada a 2: {partes_limitadas}")

Substituindo Substrings com re.sub() e re.subn()

O método sub(replacement, string[, count=0]) encontra todas as ocorrências do padrão na string e as substitui pela replacement. O parâmetro count especifica o número máximo de substituições a serem feitas.

import re

# Substitui nomes de animais por uma categoria genérica
padrao_animais = re.compile(r"(gato|cachorro|pássaro)")
texto_animais = "Eu tenho um gato, um cachorro e um pássaro."

texto_modificado_total = padrao_animais.sub("animal de estimação", texto_animais)
print(f"Texto após substituição total: {texto_modificado_total}")

texto_modificado_limitado = padrao_animais.sub("animal de estimação", texto_animais, count=1)
print(f"Texto após substituição limitada (1 vez): {texto_modificado_limitado}")

O método subn() é similar a sub(), mas retorna uma tupla contendo a nova string e o número de substituições realizadas:

import re

# Padrão para substituir números por placeholders
padrao_numeros = re.compile(r"\d+")
texto_com_numeros = "Item 1 custa 10. Item 2 custa 20."

texto_e_contagem = padrao_numeros.subn("[VALOR]", texto_com_numeros)
print(f"Resultado subn: {texto_e_contagem}")

texto_sem_numeros = "Nenhum número aqui."
resultado_sem_sub = padrao_numeros.subn("[VALOR]", texto_sem_numeros)
print(f"Resultado subn (nenhuma substituição): {resultado_sem_sub}")

Substituição com Referências a Grupos

É possível usar grupos capturados na string de substituição, referenciando-os por número (\1, \2, etc.) ou por nome (\g<nome>). Isso é extremamente útil para reformatar ou rearranjar texto.

import re

# Padrão para encontrar um nome no formato "Sobrenome, Nome" e inverter
padrao_inverter_nome = re.compile(r"(\w+),\s*(\w+)")
nome_original = "Silva, João"

nome_invertido = padrao_inverter_nome.sub(r"\2 \1", nome_original)
print(f"Nome original: '{nome_original}' -> Nome invertido: '{nome_invertido}'")

Para grupos nomeados, o uso de \g<nome> melhora a legibilidade:

import re

# Padrão para extrair um valor de configuração usando grupos nomeados
padrao_config = re.compile(r"Configuração:\s*(?P<parametro>\w+)\s*=\s*(?P<valor>[^;]+);")
linha_config = "Configuração:  DATABASE = 'prod_db'; Outro: foo=bar;"

# Usando referências a grupos numerados
substituicao_num = padrao_config.sub(r"Parâmetro '\1' com valor '\2'", linha_config)
print(f"Substituição com grupos numerados: {substituicao_num}")

# Usando referências a grupos nomeados
substituicao_nomeada = padrao_config.sub(r"Parâmetro '\g<parametro>' com valor '\g<valor>'", linha_config)
print(f"Substituição com grupos nomeados: {substituicao_nomeada}")
</valor></parametro></valor></parametro>

Quando Usar Expressões Regulares

Antes de optar por expressões regulares, é prudente avaliar se os métodos de string nativos do Python podem resolver a tarefa. Para correspondências e substituições de strings fixas, os métodos de string (como .replace(), .split(), .find()) costumam ser mais eficientes e fáceis de ler. Expressões regulares se tornam indispensáveis quando a flexibilidade e a correspondência de padrões complexos são necessárias, como validação de e-mails, análise de logs ou reformatação de dados com estruturas variáveis.

Tags: Python Expressões Regulares Módulo re processamento de texto Manipulação de String

Publicado em 6-8 21:38 por Thomas