Le module re

Apprenez à utiliser le module re de Python.

Publié le par Gabriel Trouvé (mis à jour le )

32 minutes

Besoin d'extraire les numéros de téléphone d'un document, de vérifier si l'adresse email saisie est valide ? Essayer de résoudre ces problèmes à grands coups de if, for ou split() peut vite devenir compliqué.

La solution : les expressions régulières (regex), via le module natif re.

Dans ce guide, nous allons nous concentrer sur la pratique et les fonctions les plus utiles au quotidien.

Les bases : chercher et valider

Pour chercher un motif dans une chaîne de caractères, les deux fonctions les plus courantes sont re.search() et re.match(). Voici la différence :

  • re.search() cherche le motif n'importe où dans la chaîne

  • re.match() cherche le motif uniquement au tout début de la chaîne

import re

texte = "Bonjour Patrick, ton numéro est validé."

# Cherche "Patrick" n'importe où dans le texte
resultat_search = re.search(r"Patrick", texte)
print(resultat_search)  # Trouvé ! re.Match object

# Cherche "Patrick" uniquement au début du texte
resultat_match = re.match(r"Patrick", texte)
print(resultat_match)  # None (car le texte commence par "Bonjour")
PYTHON

Si la recherche réussit, ces fonctions renvoient un objet Match qui contient des informations sur ce qui a été trouvé, sinon None. Pour retourner le texte correspondant de l'objet Match, il faut utiliser la méthode group().

import re

texte = "Bonjour Patrick, ton numéro est validé."

# Cherche "Patrick" n'importe où dans le texte
resultat_search = re.search(r"Patrick", texte)
print(resultat_search.group())  # Affiche "Patrick"
PYTHON

À noter

Vous avez remarqué le r devant la chaîne de caractères r"\n" ? C'est pour indiquer à Python qu'il s'agit d'une raw string (chaîne brute). Sans le r, Python transformerait \n en un saut de ligne avant même de le transmettre au moteur de regex. Le r permet de passer la chaîne telle quelle à la regex.

Les principaux métacaractères

Les métacaractères jouent un rôle bien spécifique au sein des regex. Nous ne listons ici que les métacaractères indispensables pour débuter. Mais il en existe bien sûr beaucoup d'autres, et vous pouvez même créer vos propres ensembles de caractères.

Les classes de caractères

  • \d : Un chiffre (de 0 à 9)

  • \w : Un caractère alphanumérique, donc n'importe quelle lettre de l'alphabet, n'importe quel chiffre ou le tiret du bas _

  • \s : Tout type d'espacement comme l'espace classique, les tabulations ou les sauts de ligne

À noter

Pour ces trois classes de caractères, leur version en majuscule donne exactement l'inverse ! Par exemple, \D correspond à tout sauf un chiffre.

Les quantificateurs

  • + : 1 fois ou plus. Par exemple, \d+ correspond à au moins un chiffre, mais potentiellement beaucoup

  • * : 0 fois ou plus. Le caractère est donc facultatif, mais on autorise une répétition infinie

  • ? : 0 ou 1 fois. Le caractère est aussi facultatif, mais sans répétition

  • {n} : Exactement n fois. \d{5} permet d'exiger 5 chiffres

  • {n,m} : Entre n et m fois. \w{2,4} permet d'autoriser entre 2 et 4 caractères

Les ancres

Elles ne représentent pas un caractère, mais une position.

  • ^ : Le tout début d'une chaîne. Si vous écrivez ^Bonjour, la regex ne trouvera le mot que s'il est le tout premier mot du texte

  • $ : La toute fin de la chaîne. Si vous écrivez Sébastien$, le mot doit être le tout dernier du texte

import re

code_postal = "60650"
# On cherche exactement 5 chiffres du début (^) à la fin ($)
if re.search(r"^\d\d\d\d\d$", code_postal):
    print("Code postal de Patrick valide !")

# Encore plus court en précisant le nombre exact avec des accolades :
if re.search(r"^\d{5}$", code_postal):
    print("Code postal de Patrick toujours valide !")
PYTHON

Trouver toutes les occurrences

Nous avons vu re.search(), mais cette dernière s'arrête dès qu'elle trouve le premier résultat. Maintenant, nous allons voir comment faire pour extraire tous les résultats d'un texte.

Le plus simple : re.findall()

Pour récupérer les correspondances sous forme de texte, re.findall() est l'idéal. Cette fonction parcourt le texte et renvoie une liste contenant toutes les correspondances.

import re

texte = "Les scores sont : Patrick 15, Sebastien 42 et Ely 8."
# On cherche tous les groupes de chiffres
scores = re.findall(r"\d+", texte)

print(scores)  # Affiche : ['15', '42', '8']
PYTHON

Parfait pour une simple extraction, mais si nous voulons obtenir plus d'informations, comme la position d'un élément, on utilisera re.finditer().

La précision avec re.finditer()

Contrairement à findall(), finditer() renvoie un itérateur d'objets Match (les mêmes que re.search() donc !). Et comme on aime la POO, on peut en profiter pour utiliser les méthodes de Match.

import re

texte = "Patrick 15, Sebastien 42"

# On itère sur chaque résultat trouvé
for resultat in re.finditer(r"\d+", texte):
    valeur = resultat.group()
    position = resultat.span()
    print(f"Nombre trouvé : {valeur}, à la position : {position}")


# Nombre trouvé : 15, à la position : (8, 10)
# Nombre trouvé : 42, à la position : (22, 24)
PYTHON

À noter

re.finditer() est plus efficace que re.findall() en matière de mémoire. En renvoyant un itérateur, les résultats ne sont pas tous chargés en mémoire d'un seul coup.

Les groupes

Trouver un motif, c'est bien, mais parfois, créer des groupes, c'est mieux ! Et c'est très simple à faire grâce aux ().

import re

numero = "Tel: 06-12-34-56-78"

# On utilise les parenthèses pour isoler l'indicatif (groupe 1) et le reste (groupe 2)
resultat = re.search(r"(0[1-9])-(.*)", numero)
# Le . correspond à n'importe quel caractère, et le * indique que ce caractère peut apparaître zéro ou plusieurs fois

if resultat:
    # .group(1) renvoie le contenu des premières parenthèses
    print(f"Indicatif : {resultat.group(1)}") # Affiche : 06
    # .group(2) renvoie le contenu des deuxièmes parenthèses
    print(f"Reste du numéro : {resultat.group(2)}") # Affiche : 12-34-56-78
PYTHON

Une petite astuce si jamais vous avez beaucoup de groupes : les groupes nommés. La syntaxe peut paraître un peu compliquée, mais on s'y fait vite : (?P<nom_du_groupe>...).

import re

# Une ligne de log typique générée par un serveur
log_serveur = "[INFO] Connexion réussie pour l'utilisateur Sebastien depuis l'IP 192.168.1.42"

# On nomme nos groupes "pseudo" et "ip" pour plus de clarté
# (Note: [\d\.]+ cherche une suite de chiffres et de points)
regex = r"utilisateur (?P<pseudo>\w+) depuis l'IP (?P<ip>[\d\.]+)"
resultat = re.search(regex, log_serveur)

if resultat:
    print(f"Utilisateur : {resultat.group('pseudo')}") # Utilisateur : Sebastien
    print(f"Adresse IP : {resultat.group('ip')}") # Adresse IP : 192.168.1.42
PYTHON

Découper du texte

Vous connaissez certainement la méthode split(), mais si les mots sont séparés aléatoirement par des virgules, des points-virgules ou des tirets, on utilise re.split().

import re

texte = "Patrick, Sebastien; Ely - Mathieu"

# On coupe à chaque fois qu'on rencontre une virgule, un point-virgule ou un tiret, 
# potentiellement suivis d'un espace (\s*)
liste_prenoms = re.split(r"[;,\-]\s*", texte)

print(liste_prenoms)
# Affiche : ['Patrick', 'Sebastien', 'Ely', 'Mathieu']
PYTHON

Remplacer du texte

Il est possible de remplacer du texte en se basant sur un motif avec re.sub().

import re

texte = "Appelez Patrick au 0601020304 ou Sebastien au 0709080706."

# On remplace toute suite de 10 chiffres consécutifs par "[CENSURÉ]"
texte_anonymise = re.sub(r"\d{10}", "[CENSURÉ]", texte)

print(texte_anonymise)
# Affiche : Appelez Patrick au [CENSURÉ] ou Sebastien au [CENSURÉ].
PYTHON

Les flags

Avec le module re, il est possible d'utiliser des flags pour modifier le comportement de votre regex. Les deux plus communs :

  • re.IGNORECASE rend la recherche insensible à la casse
import re

texte = "Le dossier de PATRICK est prêt, mais patrick est absent."

# Sans flag, on ne trouverait que l'exacte casse.
# Avec re.IGNORECASE, on trouve toutes les variations !
resultats = re.findall(r"patrick", texte, flags=re.IGNORECASE)

print(resultats)
# Affiche : ['PATRICK', 'patrick']
PYTHON
  • re.VERBOSE permet d'ignorer les espaces dans un motif et d'y ajouter des commentaires
import re

# Grâce à re.VERBOSE, on peut aérer et commenter notre regex !
motif = r"""
    ^               # Début
    0[1-9]          # Indicatif
    ([-. ]?\d{2}){4}# 4 paires de chiffres avec séparateur facultatif
    $               # Fin
"""

if re.search(motif, "06.12.34.56.78", re.VERBOSE):
    print("Numéro détecté avec une regex documentée.")
PYTHON

Sans le flag re.VERBOSE, l'ensemble serait traité littéralement, donc espaces et commentaires inclus !

Compiler ses regex

Bonne nouvelle : le module re intègre un cache interne qui conserve les dernières regex compilées. Concrètement, même dans une boucle, Python ne recompile pas votre motif à chaque appel de re.search().

Alors, pourquoi utiliser re.compile() ? C'est avant tout une question d'organisation du code 😎

  • Centraliser vos déclarations de regex en haut du fichier

  • Leur donner un nom explicite et parlant

  • Adopter une approche plus orientée objet

import re

fichiers_logs = ["Erreur 404", "Succès 200", "Erreur 500", "Avertissement 403"]

# On compile la regex une seule fois avant la boucle
regex_code = re.compile(r"\d{3}")

for ligne in fichiers_logs:
    resultat = regex_code.search(ligne)
    if resultat:
        print(f"Code statut détecté : {resultat.group()}")

# Code statut détecté : 404
# Code statut détecté : 200
# Code statut détecté : 500
# Code statut détecté : 403
PYTHON

Bravo, tu es prêt à passer à la suite

Rechercher sur le site

Inscris-toi à Docstring

Pour commencer ton apprentissage.

Tu as déjà un compte ? Connecte-toi.