Introduction
Une expression régulière, ou regex (regular expression), est un outil puissant pour le traitement de texte. C'est une séquence de caractères qui définit un modèle de recherche, vous permettant de trouver, de faire correspondre et de gérer du texte avec une flexibilité incroyable. En Python, la fonctionnalité des expressions régulières est gérée par le module re.
Dans ce laboratoire, vous apprendrez les bases de l'utilisation des expressions régulières en Python. Nous commencerons par les principes fondamentaux de la correspondance de modèles (pattern matching) et progresserons progressivement vers des sujets plus avancés tels que le regroupement (grouping), l'utilisation des quantificateurs pour la répétition, la définition des classes de caractères, et l'exploitation de méthodes et de drapeaux (flags) puissants pour gérer des scénarios de correspondance complexes. À la fin, vous serez à l'aise pour utiliser les regex afin d'effectuer des opérations de chaîne sophistiquées.
Introduction à la Correspondance Regex
Dans cette étape, vous apprendrez le flux de travail de base pour utiliser les expressions régulières (regex) en Python. Le processus implique généralement quatre étapes principales :
- Importer le module
re. - Créer un objet Regex en utilisant
re.compile(). L'utilisation d'une chaîne brute (r"...") est recommandée pour empêcher les barres obliques inverses d'être interprétées comme des caractères d'échappement. - Utiliser la méthode
search()de l'objet Regex pour trouver un motif dans une chaîne. Cela retourne un objetMatchsi le motif est trouvé. - Utiliser la méthode
group()de l'objetMatchpour obtenir le texte effectivement correspondant.
Tout d'abord, examinons quelques symboles regex courants que vous utiliserez.
| Symbole | Correspond à |
|---|---|
? |
zéro ou une occurrence du groupe précédent. |
* |
zéro ou plusieurs occurrences du groupe précédent. |
+ |
une ou plusieurs occurrences du groupe précédent. |
{n} |
exactement $n$ occurrences du groupe précédent. |
^spam |
signifie que la chaîne doit commencer par spam. |
spam$ |
signifie que la chaîne doit se terminer par spam. |
. |
n'importe quel caractère, sauf les caractères de nouvelle ligne. |
\d, \w, et \s |
respectivement un chiffre, un caractère alphanumérique (mot), ou un espace. |
\D, \W, et \S |
respectivement tout sauf un chiffre, un caractère alphanumérique, ou un espace. |
[abc] |
n'importe quel caractère entre les crochets (tel que a, b, c). |
[^abc] |
n'importe quel caractère qui n'est pas entre les crochets. |
Maintenant, mettons cela en pratique. Nous allons écrire un script pour trouver un numéro de téléphone dans une chaîne. Le motif pour un numéro de téléphone américain est XXX-XXX-XXXX, où X est un chiffre. En regex, \d représente un chiffre.
Note: Si vous n'êtes pas familier avec l'utilisation de WebIDE (similaire à VS Code), veuillez vous familiariser avec son utilisation via ce lab.
Ouvrez le fichier main.py depuis l'explorateur de fichiers à gauche et ajoutez le code suivant.
## Importe le module re pour les opérations d'expressions régulières
import re
## re.compile(): crée un objet de motif regex (utilise une chaîne brute r'' pour éviter l'échappement)
## Le motif \d\d\d-\d\d\d-\d\d\d\d correspond à trois chiffres, un tiret, trois chiffres, un tiret, et quatre chiffres.
phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
## La méthode search() analyse la chaîne, à la recherche du premier emplacement où cette regex produit une correspondance.
mo = phone_num_regex.search('My number is 415-555-4242.')
## La méthode group() retourne la chaîne du texte correspondant.
print(f'Phone number found: {mo.group()}')
Pour exécuter le script, ouvrez un terminal et exécutez la commande suivante :
python3 main.py
Vous devriez voir le numéro de téléphone correspondant s'afficher dans la console.
Phone number found: 415-555-4242
Ce flux de travail de base est le fondement de toutes les opérations regex en Python.
Groupement et Alternance
Dans cette étape, vous apprendrez comment isoler des parties spécifiques de votre texte correspondant à l'aide de groupes, et comment faire correspondre l'une parmi plusieurs expressions possibles grâce à l'alternance.
Regroupement avec les Parenthèses ()
Les parenthèses () sont utilisées pour créer des groupes au sein d'une expression régulière. Ceci est utile lorsque vous souhaitez extraire des parties d'une correspondance, comme l'indicatif régional (area code) d'un numéro de téléphone. Vous pouvez accéder aux groupes individuels avec group(n), où group(1) est le premier groupe, group(2) est le second, et ainsi de suite. group(0) ou group() retourne la chaîne complète correspondante.
Modifions main.py pour séparer l'indicatif régional du reste du numéro de téléphone.
import re
## Les parenthèses créent des groupes : group(1) pour le premier groupe, group(2) pour le second.
## Le premier groupe (\d\d\d) capture l'indicatif régional.
## Le second groupe (\d\d\d-\d\d\d\d) capture le numéro principal.
phone_num_regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')
mo = phone_num_regex.search('My number is 415-555-4242.')
## Pour récupérer tous les groupes en une seule fois, utilisez la méthode groups(), qui retourne un tuple.
area_code, main_number = mo.groups()
print(f"Area Code: {area_code}")
print(f"Main Number: {main_number}")
print(f"Full Match: {mo.group(0)}")
Exécutez le script :
python3 main.py
Le résultat affichera les parties séparées du numéro :
Area Code: 415
Main Number: 555-4242
Full Match: 415-555-4242
Alternance avec la Barre Verticale |
Le caractère barre verticale | agit comme un opérateur "OU" (OR). Il vous permet de faire correspondre une expression parmi plusieurs expressions possibles. Par exemple, Batman|Tina Fey correspondra soit à "Batman", soit à "Tina Fey".
Vous pouvez également utiliser la barre verticale à l'intérieur d'un groupe pour faire correspondre l'une de plusieurs options possibles dans le cadre de votre regex. Essayons de faire correspondre différents termes liés à "Bat-". Mettez à jour votre main.py avec le code suivant :
import re
## La barre verticale | correspond à l'un des motifs suivants dans un groupe.
## Ici, elle correspond à 'man', 'mobile', 'copter', ou 'bat'.
bat_regex = re.compile(r'Bat(man|mobile|copter|bat)')
mo = bat_regex.search('Batmobile lost a wheel')
print(f"Full match: {mo.group()}")
print(f"Group 1: {mo.group(1)}")
Exécutez à nouveau le script :
python3 main.py
Vous verrez que la regex a trouvé une correspondance avec "Batmobile", et que le premier groupe a capturé "mobile".
Full match: Batmobile
Group 1: mobile
Répétition avec Quantificateurs
Dans cette étape, nous allons explorer les quantificateurs, qui sont des caractères spéciaux vous permettant de spécifier combien de fois un groupe précédent doit se répéter.
Correspondance Optionnelle avec ?
Le point d'interrogation ? rend le groupe précédent optionnel, ce qui signifie qu'il correspondra zéro ou une fois. C'est parfait pour les motifs qui peuvent ou non apparaître. Par exemple, nous pouvons faire correspondre à la fois "Batman" et "Batwoman".
Mettez à jour main.py avec ce code :
import re
## Le groupe (wo)? est optionnel. Il correspondra à 'wo' s'il existe, ou ne correspondra à rien s'il est absent.
bat_regex = re.compile(r'Bat(wo)?man')
mo1 = bat_regex.search('The Adventures of Batman')
print(mo1.group())
mo2 = bat_regex.search('The Adventures of Batwoman')
print(mo2.group())
Exécutez le script :
python3 main.py
Le résultat montre que les deux chaînes ont été mises en correspondance avec succès :
Batman
Batwoman
Zéro ou Plusieurs avec *
L'astérisque * (ou étoile) correspond au groupe précédent zéro ou plusieurs fois. Ceci est utile lorsqu'un motif peut se répéter n'importe quel nombre de fois, y compris aucune.
import re
## Le groupe (wo)* peut apparaître zéro ou plusieurs fois.
bat_regex = re.compile(r'Bat(wo)*man')
mo1 = bat_regex.search('The Adventures of Batman') ## Zéro 'wo'
print(mo1.group())
mo2 = bat_regex.search('The Adventures of Batwowowowoman') ## Quatre 'wo'
print(mo2.group())
Un ou Plusieurs avec +
Le signe plus + correspond au groupe précédent une ou plusieurs fois. Contrairement à l'étoile, le groupe doit apparaître au moins une fois.
import re
## Le groupe (wo)+ doit apparaître au moins une fois.
bat_regex = re.compile(r'Bat(wo)+man')
mo1 = bat_regex.search('The Adventures of Batwoman') ## Un 'wo'
print(mo1.group())
mo2 = bat_regex.search('The Adventures of Batman') ## Zéro 'wo'
print(mo2 is None) ## Ceci affichera True car aucune correspondance n'est trouvée.
Répétitions Spécifiques avec {}
Les accolades {} vous permettent de spécifier un nombre exact de répétitions, ou une plage.
{3}correspond exactement trois fois.{3,5}correspond entre trois et cinq fois.{3,}correspond trois fois ou plus.{,5}correspond jusqu'à cinq fois.
import re
## (Ha){3} correspond à 'Ha' répété exactement 3 fois.
ha_regex = re.compile(r'(Ha){3}')
mo1 = ha_regex.search('HaHaHa')
print(mo1.group())
Correspondance Vorace (Greedy) et Non-Vorace (Non-Greedy)
Par défaut, la regex de Python est "vorace" (greedy), ce qui signifie qu'elle correspond à la plus longue chaîne possible. Pour la rendre "non-vorace" (non-greedy, correspondant à la plus courte chaîne possible), ajoutez un point d'interrogation ? après le quantificateur.
Voyons la différence. Effacez main.py et ajoutez ce qui suit :
import re
## Correspondance vorace : {3,5} correspond à la plus longue chaîne possible (5 'Ha').
greedy_ha_regex = re.compile(r'(Ha){3,5}')
mo1 = greedy_ha_regex.search('HaHaHaHaHa')
print(f"Greedy match: {mo1.group()}")
## Correspondance non-vorace : {3,5}? correspond à la plus courte chaîne possible (3 'Ha').
non_greedy_ha_regex = re.compile(r'(Ha){3,5}?')
mo2 = non_greedy_ha_regex.search('HaHaHaHaHa')
print(f"Non-greedy match: {mo2.group()}")
Exécutez le script :
python3 main.py
Le résultat montre clairement la différence :
Greedy match: HaHaHaHaHa
Non-greedy match: HaHaHa
Classes de Caractères, Ancrages et Caractères Génériques
Dans cette étape, vous apprendrez des techniques plus avancées, y compris comment trouver toutes les correspondances, créer des ensembles de caractères personnalisés, ancrer les correspondances au début ou à la fin d'une chaîne, et utiliser des caractères génériques (wildcards).
La Méthode findall()
Alors que search() ne retourne que la première correspondance, la méthode findall() retourne une liste de toutes les correspondances non chevauchantes dans la chaîne. Si la regex contient des groupes, findall() retourne une liste de tuples, où chaque tuple contient les chaînes pour chaque groupe.
Mettez à jour main.py avec cet exemple :
import re
## Si la regex n'a pas de groupes, findall() retourne une liste de chaînes.
phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
matches = phone_num_regex.findall('Cell: 415-555-9999 Work: 212-555-0000')
print(matches)
Exécutez le script pour voir le résultat :
python3 main.py
['415-555-9999', '212-555-0000']
Créer Vos Propres Classes de Caractères []
Les crochets [] vous permettent de définir une classe de caractères personnalisée qui correspondra à n'importe quel caractère unique se trouvant à l'intérieur. Par exemple, [aeiou] correspond à n'importe quelle voyelle minuscule. Vous pouvez également spécifier une plage, comme [a-z] pour n'importe quelle lettre minuscule ou [0-9] pour n'importe quel chiffre.
Un accent circonflexe ^ comme premier caractère à l'intérieur des crochets crée une classe de caractères négative, qui correspond à tout caractère qui n'est pas dans la classe.
Trouvons toutes les voyelles dans une chaîne. Mettez à jour main.py :
import re
## [aeiouAEIOU] correspond à n'importe quelle voyelle, minuscule ou majuscule.
vowel_regex = re.compile(r'[aeiouAEIOU]')
vowels = vowel_regex.findall('Robocop eats baby food. BABY FOOD.')
print(vowels)
Ancrages : Accent Circonflexe ^ et Dollar $
Les ancrages (anchors) sont utilisés pour affirmer une position dans la chaîne.
- L'accent circonflexe
^correspond au début de la chaîne. - Le signe dollar
$correspond à la fin de la chaîne.
Lorsqu'ils sont utilisés ensemble (^...$), ils forcent la chaîne entière à correspondre au motif.
import re
## ^Hello correspond aux chaînes qui commencent par 'Hello'.
begins_with_hello = re.compile(r'^Hello')
match1 = begins_with_hello.search('Hello world!')
print(match1 is not None) ## True
## \d+$ correspond aux chaînes qui se terminent par un ou plusieurs chiffres.
ends_with_number = re.compile(r'\d+$')
match2 = ends_with_number.search('Your number is 42')
print(match2.group()) ## '42'
## ^\d+$ correspond aux chaînes qui ne sont composées que de chiffres.
whole_string_is_num = re.compile(r'^\d+$')
match3 = whole_string_is_num.search('1234567890')
print(match3 is not None) ## True
Le Caractère Générique .
Le point . (ou caractère générique, wildcard) correspond à n'importe quel caractère unique, sauf un saut de ligne (newline). C'est un raccourci utile pour "n'importe quel caractère".
Trouvons tous les mots de trois lettres se terminant par "at". Mettez à jour main.py :
import re
## Le caractère . correspond à n'importe quel caractère (sauf saut de ligne).
## Donc, .at correspond à 'cat', 'hat', 'sat', etc.
at_regex = re.compile(r'.at')
matches = at_regex.findall('The cat in the hat sat on the flat mat.')
print(matches)
Exécutez le script :
python3 main.py
Le résultat sera une liste de tous les mots correspondants :
['cat', 'hat', 'sat', 'lat', 'mat']
Méthodes Avancées et Indicateurs (Flags)
Dans cette dernière étape, nous allons aborder des méthodes et des drapeaux (flags) puissants qui modifient le comportement de vos expressions régulières (regex), les rendant encore plus polyvalentes.
Correspondance de Tout avec Point-Étoile .*
La combinaison .* est un motif courant qui correspond à n'importe quel caractère (.) répété zéro ou plusieurs fois (*). Elle est utilisée pour correspondre à n'importe quelle séquence de caractères. Par défaut, elle est vorace (greedy) et correspondra au plus grand texte possible.
import re
## Le motif (.*) capture tout ce qui se trouve entre 'First Name: ' et ' Last Name: '.
name_regex = re.compile(r'First Name: (.*) Last Name: (.*)')
mo = name_regex.search('First Name: Al Last Name: Sweigart')
print(f"First Name: {mo.group(1)}")
print(f"Last Name: {mo.group(2)}")
Pour la rendre non-vorace (non-greedy), utilisez .*?. Cela correspondra à la plus courte chaîne possible.
Correspondance des Sauts de Ligne avec re.DOTALL
Par défaut, le caractère point . ne correspond pas aux caractères de saut de ligne (\n). Pour changer cela, vous pouvez passer re.DOTALL comme deuxième argument à re.compile(). Ce drapeau permet au . de correspondre à tous les caractères, y compris les sauts de ligne.
import re
## Avec re.DOTALL, '.*' correspondra sur plusieurs lignes.
newline_regex = re.compile('.*', re.DOTALL)
mo = newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.')
print(mo.group())
Correspondance Insensible à la Casse avec re.IGNORECASE
Pour que votre regex ignore la casse (par exemple, correspondre à "cat", "Cat" et "CAT"), passez re.IGNORECASE ou son raccourci re.I comme deuxième argument à re.compile().
import re
## re.I permet au motif 'robocop' de correspondre à 'Robocop', 'ROBOCOP', etc.
robocop = re.compile(r'robocop', re.I)
match = robocop.search('Robocop is part man, part machine, all cop.')
print(match.group())
Substitution de Chaînes avec la Méthode sub()
La méthode sub() trouve toutes les occurrences d'un motif et les remplace par une chaîne spécifiée. Elle prend deux arguments : la chaîne de remplacement et la chaîne à rechercher.
Remplaçons les noms d'agents par "CENSORED". Mettez à jour main.py :
import re
## Le motif 'Agent \w+' correspond à 'Agent ' suivi d'un ou plusieurs caractères alphanumériques.
names_regex = re.compile(r'Agent \w+')
result = names_regex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.')
print(result)
Exécutez le script :
python3 main.py
Le résultat affichera la chaîne substituée :
CENSORED gave the secret documents to CENSORED.
Gestion des Regex Complexes avec re.VERBOSE
Lorsque les motifs de regex deviennent complexes, ils peuvent être difficiles à lire. En passant re.VERBOSE à re.compile(), vous pouvez écrire votre regex sur plusieurs lignes et ajouter des commentaires pour expliquer les différentes parties. Les espaces et les commentaires (#) à l'intérieur de la chaîne sont ignorés.
Cette regex complexe :
phone_regex = re.compile(r'((\d{3}|\(\d{3}\))?(\s|-|\.)?\d{3}(\s|-|\.)\d{4}(\s*(ext|x|ext.)\s*\d{2,5})?)')
Devient cette version lisible avec re.VERBOSE :
import re
phone_regex = re.compile(r'''(
(\d{3} | \(\d{3}\))? ## area code (optional)
(\s | - | \.)? ## separator (optional)
\d{3} ## first 3 digits
(\s | - | \.) ## separator
\d{4} ## last 4 digits
(\s*(ext|x|ext.)\s*\d{2,5})? ## extension (optional)
)''', re.VERBOSE)
mo = phone_regex.search("My phone number is 415-555-4242 ext. 55")
if mo:
print(f"Found: {mo.group()}")
Ceci rend votre code beaucoup plus maintenable.
Résumé
Dans ce laboratoire, vous avez maîtrisé les fondamentaux des expressions régulières (regular expressions) en Python en utilisant le module re. Vous avez appris le flux de travail complet, de la compilation d'un motif avec re.compile() à la recherche de correspondances avec search() et à la récupération du texte avec group().
Vous avez exploré des concepts clés, notamment :
- Le groupement (Grouping) avec
()pour capturer des parties spécifiques d'une correspondance. - L'alternance (Alternation) avec
|pour correspondre à un parmi plusieurs motifs. - Les quantificateurs (Quantifiers) comme
?,*,+, et{}pour contrôler la répétition. - La correspondance vorace (Greedy) contre non-vorace (Non-greedy) pour contrôler la longueur des correspondances.
- Les outils avancés comme
findall()pour obtenir toutes les correspondances, les classes de caractères[]pour les ensembles personnalisés, et les ancrages^et$pour fixer les correspondances aux limites de la chaîne. - Les drapeaux (flags) puissants tels que
re.DOTALLpour la correspondance multiligne,re.IGNORECASEpour l'insensibilité à la casse, etre.VERBOSEpour écrire des regex propres et commentées. - La substitution de chaînes avec la méthode
sub().
Avec ces compétences, vous êtes maintenant équipé pour gérer un large éventail de tâches de traitement de texte, allant de la validation de données simples à l'extraction et à la manipulation complexes de données.



