Introdução: Por que usar expressões regulares em Rust?
Expressões regulares (regex) são uma ferramenta poderosa para buscar, extrair e substituir texto com base em padrões. Em Rust, o trabalho com expressões regulares é implementado através da crate regex, considerada uma das mais rápidas e seguras do ecossistema. Ao contrário de muitas outras linguagens, o compilador Rust verifica a validade da expressão regular em tempo de compilação (se usar a macro regex!), o que ajuda a evitar erros em tempo de execução.
Neste artigo, exploraremos exemplos práticos de uso de expressões regulares em Rust: desde a busca básica até a extração complexa de dados. Você aprenderá a configurar o projeto, escrever padrões e processar os resultados.
1. Configuração do projeto e primeira regex
Adicionando a dependência
Para começar, adicione a crate regex ao arquivo Cargo.toml:
[dependencies]regex = "1"Depois disso, atualize as dependências com o comando cargo build.
Primeiro exemplo: busca de palavra
Vamos escrever um programa simples que encontra a palavra "Rust" em uma string:
use regex::Regex;
fn main() { let re = Regex::new(r"Rust").unwrap(); let text = "Estou aprendendo Rust e Python."; if re.is_match(text) { println!("Palavra 'Rust' encontrada!"); } else { println!("Palavra não encontrada."); }}A função Regex::new compila o padrão. Se o padrão for inválido, o programa será encerrado com erro (use unwrap() apenas em protótipos).
2. Extração de dados com capturas
Agrupamento e grupos nomeados
Expressões regulares permitem extrair partes do texto usando parênteses. Vejamos um exemplo com email:
use regex::Regex;
fn main() { let re = Regex::new(r"(?P<name>[a-zA-Z0-9._%+-]+)@(?P<domain>[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})").unwrap(); let text = "Meu email: user@example.com, e também admin@test.org."; for caps in re.captures_iter(text) { println!("Nome: {}", &caps["name"]); println!("Domínio: {}", &caps["domain"]); }}Neste exemplo, usamos grupos nomeados (?P<name>...). Isso torna o código mais legível e conveniente para processamento posterior.
Extraindo todas as correspondências
Se precisar obter todas as correspondências sem grupos, use find_iter:
use regex::Regex;
fn main() { let re = Regex::new(r"\\d+").unwrap(); let text = "Preços: 100, 250 e 500 reais."; for mat in re.find_iter(text) { println!("Número encontrado: {}", mat.as_str()); }}O método find_iter retorna um iterador sobre objetos Match, que contêm as posições inicial e final, além do próprio texto da correspondência.
3. Substituição de texto e padrões complexos
Substituição simples
Vamos substituir todos os dígitos pela palavra "NÚMERO":
use regex::Regex;
fn main() { let re = Regex::new(r"\\d+").unwrap(); let text = "Código: 12345, status: 0."; let result = re.replace_all(text, "NÚMERO"); println!("{}", result); // "Código: NÚMERO, status: NÚMERO."}Substituição com capturas
Você pode referenciar grupos na string de substituição usando $1, $2 ou grupos nomeados $name:
use regex::Regex;
fn main() { let re = Regex::new(r"(?P<year>\\d{4})-(?P<month>\\d{2})-(?P<day>\\d{2})").unwrap(); let text = "Data: 2024-03-15"; let result = re.replace_all(text, "$day.$month.$year"); println!("{}", result); // "Data: 15.03.2024"}Padrão complexo: parsing de logs
Vamos considerar um exemplo real — parsing de uma linha de log de servidor web:
use regex::Regex;
fn main() { let log_line =