Introduction : Pourquoi les expressions régulières en Rust ?
Les expressions régulières (regex) sont un outil puissant pour rechercher, extraire et remplacer du texte selon un motif. En Rust, le travail avec les expressions régulières est implémenté via la crate regex, considérée comme l'une des plus rapides et des plus sûres de l'écosystème. Contrairement à de nombreux autres langages, le compilateur Rust vérifie la validité de l'expression régulière à la compilation (si l'on utilise la macro regex!), ce qui permet d'éviter des erreurs lors de l'exécution.
Dans cet article, nous allons analyser des exemples pratiques d'utilisation des expressions régulières en Rust : de la recherche de base à l'extraction complexe de données. Vous apprendrez à configurer un projet, à écrire des motifs et à traiter les résultats.
1. Configuration du projet et première regex
Ajout de la dépendance
Pour commencer, ajoutez la crate regex dans le fichier Cargo.toml :
[dependencies]regex = "1"Ensuite, mettez à jour les dépendances avec la commande cargo build.
Premier exemple : recherche d'un mot
Écrivons un programme simple qui trouve le mot "Rust" dans une chaîne :
use regex::Regex;
fn main() { let re = Regex::new(r"Rust").unwrap(); let text = "J'apprends Rust et Python."; if re.is_match(text) { println!("Le mot 'Rust' a été trouvé !"); } else { println!("Mot non trouvé."); }}La fonction Regex::new compile le motif. Si le motif est invalide, le programme se termine avec une erreur (n'utilisez unwrap() que dans des prototypes).
2. Extraction de données avec des captures
Groupes et groupes nommés
Les expressions régulières permettent d'extraire des parties de texte à l'aide de parenthèses. Prenons un exemple avec un 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 = "Mon email : user@example.com, ainsi que admin@test.org."; for caps in re.captures_iter(text) { println!("Nom : {}", &caps["name"]); println!("Domaine : {}", &caps["domain"]); }}Dans cet exemple, nous utilisons des groupes nommés (?P<name>...). Cela rend le code plus lisible et pratique pour un traitement ultérieur.
Extraction de toutes les correspondances
Si vous souhaitez obtenir toutes les correspondances sans groupes, utilisez find_iter :
use regex::Regex;
fn main() { let re = Regex::new(r"\\d+").unwrap(); let text = "Prix : 100, 250 et 500 roubles."; for mat in re.find_iter(text) { println!("Nombre trouvé : {}", mat.as_str()); }}La méthode find_iter retourne un itérateur sur des objets Match, qui contiennent les positions de début et de fin, ainsi que le texte de la correspondance.
3. Remplacement de texte et motifs complexes
Remplacement simple
Remplaçons tous les chiffres par le mot "NOMBRE" :
use regex::Regex;
fn main() { let re = Regex::new(r"\\d+").unwrap(); let text = "Code : 12345, statut : 0."; let result = re.replace_all(text, "NOMBRE"); println!("{}", result); // "Code : NOMBRE, statut : NOMBRE."}Remplacement avec utilisation des captures
Vous pouvez faire référence aux groupes dans la chaîne de remplacement avec $1, $2 ou les groupes nommés $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 = "Date : 2024-03-15"; let result = re.replace_all(text, "$day.$month.$year"); println!("{}", result); // "Date : 15.03.2024"}Motif complexe : analyse de logs
Prenons un exemple concret — l'analyse d'une ligne de log de serveur web :
use regex::Regex;
fn main() { let log_line =