15 Les expressions rationnelles
Une expression rationnelle, expressions régulières (anglicisme de Regular Expressions) ou simplement regex, décrit la forme d’un texte recherché.
Elle sert notamment à :
- rechercher une information dans un texte ;
- vérifier qu’une valeur respecte un format ;
- extraire ou remplacer les parties reconnues.
Une regex devient utile quand une partie peut varier :
CMD-2048 CMD-0007 CMD-9312
même forme, chiffres différents
Utiliser les regex consiste avant tout à définir un motif décrivant la chaine de caractères recherchée. C’est la partie la plus complexe. Ce document n’est qu’une introduction à la définition de ces motifs.
En Python, les expression rationnelles sont gérées par le module re
15.1 Le vocabulaire
texte : "Commande CMD-2048 expédiée"
motif : r"CMD-\d{4}"
└── correspondance : "CMD-2048"
- Le texte contient les données à examiner.
- Le motif décrit ce que l’on cherche.
- Une correspondance est une portion du texte reconnue par le motif.
Un motif combine des caractères ordinaires, comme CMD-, et des symboles spéciaux, comme \d.
15.2 Définir un motif
Utiliser les RegEx consiste avant tout à définir un motif décrivant la chaine de caractères à rechercher ou valider. Ce motif est lui-même une chaine de caractères. Le moteur de RegEx cherchera à identifier ce motif dans une chaine de caractères.
La suite de ce document présente une introduction à la définition d’un motif. La documentation citée en référence est bien plus exhaustive.
15.2.1 Un motif de base
Le motif le plus simple est une chaine de caractères strict. 'CMD-' par exemple peut être sera identifié dans la chaine de caractères 'CMD-' mais aussi 'CMD-2048' ou 'Commande CMD-2048'.
15.2.2 Les classes de caractères
Une classe représente un caractère parmi plusieurs possibilités.
| Motif | Signification | Exemple reconnu |
|---|---|---|
[abc] |
a, b ou c |
b |
[A-Z] |
une capitale de A à Z |
M |
\d |
un chiffre | 7 |
\w |
une lettre, un chiffre ou _ |
p |
\s |
un espace, une tabulation ou un saut de ligne | espace |
[^0-9] |
un caractère qui n’est pas un chiffre | X |
\D, \W et \S sont les formes inverses de \d, \w et \s.
Le point '.' représente presque n’importe quel caractère.
15.2.3 Nombre d’occurrences
Un quantificateur indique combien de fois l’élément précédent peut apparaître.
| Motif | Nombre attendu | Exemple |
|---|---|---|
\d? |
zéro ou une fois | 7 ou rien |
\d* |
zéro fois ou plus | 123 ou rien |
\d+ |
une fois ou plus | 123 |
\d{4} |
exactement 4 fois | 2048 |
\d{2,4} |
de 2 à 4 fois | 20, 204, 2048 |
CMD-\d{4}
│ └──── exactement quatre chiffres
└──────── texte attendu tel quel
15.2.4 La position d’un motif
Les ancres indiquent où le motif doit se trouver.
^en début de motif représente le début de la chaîne.$en fin de motif représente la fin de la chaîne.
pattern = r"^CMD-\d{4}$"
bool(re.search(pattern, "CMD-2048")) # True
bool(re.search(pattern, "Commande CMD-2048")) # False15.2.5 Les groupes de caractères
Les parenthèses regroupent plusieurs éléments :
re.fullmatch(r"(ha)+", "hahaha") # correspondanceLe symbole | signifie « ou ».
re.fullmatch(r"oui|non", "oui") # correspondance15.2.6 Référence précédente
À l’aide d’un metacaractère appelé backreference, nous pouvons matcher un groupe capturé précédemment. Ce metacaractère s’écrit \n où n est un entier qui représente le match du nième groupe capturé.
Ce metacaractère permet d’éviter le problème avec un pattern comme ([rf]i)+ qui capture 'riri', 'fifi' mais aussi 'rifi' et 'firi'.
Pour identifier spécifiquement 'riri' ou 'fifi' mais ni 'rifi' ou 'firi', le pattern est ([rf]i)\1.
En passant, pour ajouter 'loulou' aux matchs possibles, nous pouvons ajouter le ou : ([rf]i|lou)\1.
15.3 Utiliser le module re
Pour utiliser les RegEx, Python fournit le module re.
15.3.1 Les fonctions de recherche
Le module re propose les fonctions de recherche suivantes :
| Fonction | Zone examinée | |
|---|---|---|
re.search(motif, texte) |
n’importe où dans le texte | |
re.match(motif, texte) |
uniquement au début | |
re.fullmatch(motif, texte) |
toute la chaîne |
En pratique, utilisez search() pour trouver et fullmatch() pour valider.
re.search() retournera la première occurence trouvée. Pour recherobtenir toutes les occurences, vous avez :
| Fonction | Renvoie | |
|---|---|---|
re.findall(motif, texte) |
La liste des chaines de caractères trouvées | |
re.finditer(motif, texte) |
Un itérateur sur des Match Objects |
Une RegEx peut également être utilisée comme un objet. Un motif utilisé plusieurs fois peut être compilé :
cmd_ref = re.compile(r"CMD-\d{4}")
cmd_ref.search("Commande CMD-2048")
cmd_ref.fullmatch("CMD-0007")La variable cmd_ref regroupe le motif et ses options. L’objet propose les mêmes méthodes de recherche que les fonctions du module.
Notez qu’un motif compilé n’est pas plus performant qu’utiliser la fonction. Si la compilation optimise la recherche, il en va de même au niveau du module pour les fonctions.
15.4 Les Match Objects et leurs attributs
Un Match Object expose principalement les 3 méthodes suivantes. Le paramètre optionnel subgroup est un entier ou une chaine de caractères.
group([subgroup]): utilisé sans paramètre ou avec la valeur0, retourne le motif trouvé, sinon le motif correspondant au sous-groupe.start([subgroup]): utilisé sans paramètre ou avec la valeur0, retourne l’indice de début du motif trouvé, sinon celui correspondant au sous-groupe.end([subgroup]): utilisé sans paramètre ou avec la valeur0, retourne l’indice de fin du motif trouvé, sinon celui correspondant au sous-groupe.
Un sous-groupe représente un groupe défini par des parenthèses.
15.5 Les sous-groupes
texte = "Livraison prévue le 07/11/2026"
pattern = r"(\d{2})/(\d{2})/(\d{4})"
m = re.search(motif, texte)
if m:
print(m.group(0)) # 07/11/2026 : motif complet
print(m.group(1)) # 07 : premier groupe
print(m.groups()) # ('07', '11', '2026')La seconde fonction des groupes est donc d’isoler des parties d’intérêt.
Utiliser un nom rend l’extraction plus explicite qu’un numéro. Nous définissons une étiquette grâce à la syntaxe (?P<name>…).
pattern = (r"(?P<jour>\d{2})/(?P<mois>\d{2})/(?P<annee>\d{4})")
m = re.search(pattern, "Livraison le 07/11/2026")
if m:
print(m.group("annee")) # 2026
print(m.groupdict()) # {'jour': '07', 'mois': '11', 'annee': '2026'}15.5.1 Groupes non-capturants
Quand un groupe sert seulement à structurer le motif, (?:...) évite de le capturer :
pattern = r"(?:M|Mme) Dupont"(?:M|Mme) choisit une alternative sans créer de groupe à extraire.
15.6 Les substitutions
Les RegEx peuvent être utilisées pour substituer du texte grâce à :
- La fonction
sub(pattern, repl, string, count=0, flags=0) - la méthode
sub(repl, string, count=0, flags=0)
Les paramètres sont :
replpeut-être soit une chaine de substitution, soit un callable, c’est à dire une fonction. Celle-ci prendra en paramètre un match object et devra retourner une chaine de caractères.countpermet de limiter le nombre de substitutions.
Le code suivant illustre la substitution avec une chaine de caractères fixe.
ma_chaine = 'pi vaut 3.14 et e vaut 2.72'
float_regex.sub('quelque chose', ma_chaine)
# Retourne : 'pi vaut quelque chose et e vaut quelque chose'Le code suivant illustre une substitution plus intelligente avec une fonction.
temp_pattern = r"(?P<temp>[0-9]+\.[0-9]{2})°(?P<value>c|F)"
t_regex = re.compile(temp_pattern)
def simplify(temp_match):
temp = float(temp_match.group('temp'))
if temp > 30:
return "Trop chaud"
elif temp < 15:
return "Trop froid"
else:
return temp_match.group()
t_regex.sub(simplify, r"Il fait 17.08°c")
# Retourne: 'Il fait 17.08°c'
t_regex.sub(simplify, r"Il fait 32.08°c")
# Retourne: 'Il fait Trop chaud'
t_regex.sub(simplify, r"Il fait 12.08°c")
# Retourne: 'Il fait Trop froid'