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 à :

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"))  # False

15.2.5 Les groupes de caractères

Les parenthèses regroupent plusieurs éléments :

re.fullmatch(r"(ha)+", "hahaha")  # correspondance

Le symbole | signifie « ou ».

re.fullmatch(r"oui|non", "oui")  # correspondance

15.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 valeur 0, retourne le motif trouvé, sinon le motif correspondant au sous-groupe.
  • start([subgroup]) : utilisé sans paramètre ou avec la valeur 0, retourne l’indice de début du motif trouvé, sinon celui correspondant au sous-groupe.
  • end([subgroup]) : utilisé sans paramètre ou avec la valeur 0, 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 :

  • repl peut-ê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.
  • count permet 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'