Les expressions régulières en Python : chercher un motif dans du texte

Une expression régulière décrit la forme d'un texte plutôt que son contenu exact. Le module re de Python cherche, extrait et remplace à partir de ce motif.
8 min de lecture
Believemy logo

Définition

Il faut sans cesse retrouver quelque chose dans un texte sans en connaître la valeur à l'avance : la date au milieu d'une ligne de journal, le numéro dans l'objet d'un e-mail, le code postal perdu dans une adresse. Chercher un mot connu ne pose aucun problème, l'opérateur in y suffit. Mais ici, on ignore ce que l'on cherche : on sait seulement à quoi cela ressemble.

C'est ce manque que vient combler une expression régulière. C'est un motif : une courte formule qui décrit la forme d'un texte plutôt que son contenu. « Quatre chiffres, un tiret, deux chiffres, un tiret, deux chiffres » est un motif, et 2026-08-19 est l'un des textes qui lui correspondent.

Python range ces motifs dans le module re, livré avec la bibliothèque standard : rien à installer avec pip, un import re en tête de fichier suffit.

PYTHON
import re

texte = "Commande 4821 expédiée le 2026-08-19"

# Quatre chiffres, un tiret, deux chiffres, un tiret, deux chiffres
resultat = re.search(r"\d{4}-\d{2}-\d{2}", texte)
print(resultat.group())  # 2026-08-19

Tout se joue dans la chaîne passée en premier argument. \d signifie « un chiffre », {4} signifie « quatre fois de suite », et le tiret ne signifie rien de particulier : il se cherche tel quel. Un motif mêle donc en permanence des caractères pris au pied de la lettre et des symboles qui décrivent une famille ou une répétition.

Cette syntaxe n'appartient pas à Python. À quelques variantes près, la même s'écrit en JavaScript, en PHP, dans grep ou dans le champ de recherche d'un éditeur de code : ce qui s'apprend ici resservira ailleurs pendant des années.


Ce qu'elle remplace, et ce qu'elle ne doit pas remplacer

Une expression régulière remplace une chose précise : les tests et les découpages écrits à la main pour attraper un élément dont la position change d'une ligne à l'autre. Extraire un numéro de facture de l'objet d'un e-mail, relever les codes postaux d'un export, vérifier qu'une référence produit a bien la forme attendue.

Sans motif, ce travail se fait à coups de split imbriqués, et le code devient illisible dès la deuxième exception rencontrée dans les données : une ligne où l'espace manque, une autre où le champ est vide. Le motif encaisse ces variations parce qu'il décrit une forme et non une position.

En revanche, il ne remplace pas les méthodes de chaîne, qui sur un besoin fixe se lisent mieux et s'exécutent plus vite. Voici comment répartir les cas les plus courants.

Le besoinL'outil juste
Savoir si un mot figure dans un texteL'opérateur in
Découper sur un séparateur fixesplit
Vérifier un début ou une finstartswith, endswith
Retrouver une forme variableUne expression régulière
Lire du HTML, du JSON ou du CSVL'analyseur du format, jamais un motif
Attention

La dernière ligne est celle qui coûte le plus cher quand on l'ignore. HTML, JSON et CSV acceptent des imbrications, des guillemets échappés et des sauts de ligne au milieu d'une valeur : un motif qui passe sur trois exemples se casse sur le quatrième fichier réel, une fois les données déjà traitées.

Le module json, le module csv ou un analyseur HTML connaissent les règles du format et les appliquent toutes, en une ligne. Et si les données sont déjà rangées dans un tableau pandas, .str.extract applique un motif à une colonne entière : un motif travaille sur des valeurs, jamais sur une structure.


Les fonctions du module re

Le module expose une douzaine de fonctions, mais sept couvrent presque tous les usages. Toutes prennent le motif en premier argument et le texte en second ; ce qui les distingue, c'est ce qu'elles renvoient, donc la colonne de droite.

AppelCe qu'il renvoie
re.searchLe premier passage trouvé n'importe où, ou None
re.matchLa même chose, mais seulement en début de texte
re.fullmatchUn résultat si le texte entier correspond
re.findallLa liste de tous les passages trouvés
re.finditerUn itérateur sur les résultats complets
re.subLe texte avec les passages remplacés
re.splitLe texte découpé sur le motif

Les deux premières lignes sont la première cause de perte de temps chez qui découvre la bibliothèque. re.match ancre la recherche au tout premier caractère : chercher "chat" avec match dans "le chat dort" ne renvoie rien, et l'on passe un quart d'heure à réparer un motif qui n'a aucun défaut.

Deux réflexes suffisent. Pour trouver quelque chose à l'intérieur d'un texte, re.search. Pour vérifier qu'un texte entier a la bonne forme, un code postal saisi dans un formulaire par exemple, re.fullmatch. re.match ne se justifie que là où le début du texte compte vraiment.

Bon à savoir

Ces fonctions acceptent toutes un argument flags : re.IGNORECASE ignore la casse, re.MULTILINE fait porter ^ et $ sur chaque ligne plutôt que sur le texte entier, et re.VERBOSE autorise espaces et commentaires dans le motif, seul moyen de garder un motif long relisible.

Essayez un motif sur du texte
Exemples :
Facture 2026-08-19 : 3 articles, total 149,90 EUR. Contact : marie@believemy.com, commande REF-4471 du 12/06/2026.

10 correspondances. Les parties surlignées sont ce que findall renverrait. Les groupes entre parenthèses sont extraits séparément, ce qui sert à récupérer une partie de ce qu'on a trouvé.

Le moteur utilisé ici est celui du navigateur, très proche de celui de Python sur les motifs courants mais pas identique. Python nomme ses groupes (?P<nom>...) là où le navigateur écrit (?<nom>...), et le module re propose des drapeaux que cette démonstration n'expose pas.


Le résultat n'est pas le texte trouvé

Un piège attend juste après la première recherche réussie, et il ne se déclare qu'en production. Une recherche ne renvoie pas une chaîne mais un objet de correspondance, qui porte le passage trouvé, sa position et ses groupes. Et quand le motif ne trouve rien, elle renvoie None.

Enchaîner .group() sur ce retour fonctionne donc tant que le texte contient ce qui est attendu. Le jour où un e-mail arrive sans numéro de facture, c'est None qui reçoit l'appel : une AttributeError est levée et le script s'arrête. Le test qui l'évite tient en une ligne.

PYTHON
resultat = re.search(r"facture (\d+)", objet)

if resultat:                     # le test qui manque presque toujours
    numero = resultat.group(1)   # ce qu'ont capturé les parenthèses
else:
    numero = None

Les parenthèses jouent ici un second rôle : elles délimitent un groupe, le morceau du résultat que l'on veut récupérer à part. group(1) renvoie ce que la première paire a capturé, group(0) le passage entier. Au-delà de deux ou trois groupes, comptez plutôt sur des noms : (?P<numero>\d+) se relit sans compter les parenthèses et alimente groupdict(), qui rend un dictionnaire prêt à l'emploi.

Reste un détail d'écriture qui n'en est pas un : le préfixe r devant le motif. Sans lui, Python interprète les antislashs pour son propre compte avant que le moteur ne les voie, et le motif lui arrive abîmé. Écrire r"..." systématiquement supprime une famille entière de bugs incompréhensibles.


Gourmand par défaut

Un dernier comportement du moteur explique la plupart des motifs qui « attrapent trop ». Les quantificateurs * et + sont gourmands : ils avalent le plus de texte possible, puis reculent juste assez pour que la fin du motif corresponde.

PYTHON
ligne = 'nom="Dupont" ville="Lyon"'

re.findall(r'".*"', ligne)    # gourmand  : ['"Dupont" ville="Lyon"']
re.findall(r'".*?"', ligne)   # paresseux : ['"Dupont"', '"Lyon"']

Le premier motif ne s'arrête pas au guillemet qui ferme Dupont : il file jusqu'au dernier guillemet de la ligne. Le point d'interrogation rend le second paresseux, autrement dit il s'arrête à la première occasion, et les deux valeurs ressortent séparément.

Une écriture souvent meilleure consiste à interdire explicitement le caractère de fin, ici r'"[^"]*"' : le motif ne peut plus déborder et le moteur n'a aucun retour en arrière à faire. Sur un texte long, des quantificateurs imbriqués comme (a+)+ font passer un programme de quelques millisecondes à plusieurs minutes, et si ce texte vient d'un formulaire public, une seule requête suffit à immobiliser le serveur.


Questions fréquentes

Question

Pourquoi mon motif fonctionne-t-il sur un testeur en ligne et pas dans mon code ?

Trois causes, dans l'ordre de fréquence. Le préfixe r a été oublié, et le motif arrive abîmé jusqu'au moteur. Ou bien re.match a été employé là où re.search était attendu, et la recherche reste collée au premier caractère. Ou enfin les options que les testeurs activent d'office, la recherche globale et le mode multiligne, ne sont pas passées explicitement en Python.

Question

Faut-il compiler ses motifs avec re.compile ?

Rarement pour la vitesse : le module garde en cache les derniers motifs utilisés, si bien que le gain reste invisible dans une boucle ordinaire. re.compile sert surtout à donner un nom au motif et à le poser une fois pour toutes en haut du fichier, avec ses options, au lieu de le répéter à trois endroits. C'est d'abord un choix de lisibilité.

Question

Comment valider une adresse e-mail avec une expression régulière ?

Le plus honnêtement possible, c'est-à-dire à peine. La spécification autorise des formes que personne n'écrit jamais, et le motif qui les couvre toutes fait plusieurs milliers de caractères sans être juste pour autant. En pratique, vérifiez qu'il y a un seul arobase, du texte de part et d'autre et un point après, puis envoyez un message de confirmation. Seul le message qui arrive prouve que l'adresse existe.

Termes connexes

Découvrez notre glossaire Python

Parcourez les termes et définitions les plus couramment utilisés dans le domaine du développement avec Python.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.