Question 1Que retourne re.match(r"\d+", "abc 123") ?
Expressions régulières re — Recherche et substitution de motifs
Apprends le module re de Python depuis la base. Couvre quand utiliser re.match / re.search / re.findall, comment combiner les métacaractères \d / \w / \s / * / + / ?, capturer des groupes avec ( ), substituer avec re.sub, et réutiliser des motifs avec re.compile — avec des exercices pratiques exécutables.
Cet article parcourt le module re pour les expressions régulières — « extraire et remplacer les sous-chaînes qui correspondent à un motif spécifique ». Des choses que tu fais constamment dans des projets réels — analyser numéros de téléphone, emails, lignes de log et URLs — deviennent des one-liners.
Un outil pour tester du regex en direct
Les expressions régulières ont beaucoup d'éléments mobiles et sont difficiles à raisonner uniquement de tête. Pour vérifier que ton motif correspond à ce que tu attendais, le Regex Extractor tourne entièrement dans le navigateur — tape un motif et du texte et vois les correspondances en temps réel. Le garder ouvert à côté de cet article rend le suivi beaucoup plus facile.
match, search et findall — Trois fonctions de recherche et quand utiliser chacune
Le module re expose plusieurs fonctions de recherche, et tu choisis parmi trois selon le besoin. Les noms sont descriptifs — match matche au début, search cherche une correspondance n'importe où, et findall les trouve toutes. Les plages de recherche, types de retour et comportements sans correspondance sont résumés dans la prochaine table.
| Fonction | Plage de recherche | Retour | Sans correspondance |
|---|---|---|---|
| re.match | Début de la chaîne uniquement | Match object | None |
| re.search | Première correspondance n'importe où | Match object | None |
| re.findall | Toutes les correspondances | Liste de chaînes | Liste vide [] |
Du Match object retourné par re.match et re.search (un objet contenant la position du match, la chaîne matchée et les infos de groupe), tu lis la chaîne matchée en appelant sa méthode `.group()` — m.group() ou m.group(0) pour le match complet, et (avec les groupes de capture introduits plus loin) m.group(1) pour juste ce qui était entre parenthèses. Seul re.findall retourne directement une liste, donc tu n'appelles pas .group() dessus.
| Métacaractère | Sens | Exemple |
|---|---|---|
| \d | Un seul chiffre (0-9) | \d+ → un ou plusieurs chiffres |
| \w | Un caractère de mot (alphanumérique + underscore) | \w+ → IDs et mots-clés |
| \s | Un caractère blanc (espace / tab / saut de ligne) | Séparateurs |
| . | N'importe quel caractère sauf saut de ligne | Joker |
| * | Zéro ou plus du précédent | a* → vide aussi OK |
| + | Un ou plus du précédent | a+ → au moins un |
| ? | Zéro ou un du précédent | Optionnel |
| [abc] | Un parmi a / b / c | Choix |
| ^ / $ | Début / fin de chaîne | Ancres |
import re
text = "user_id: 12345, age: 30"
# match: depuis le début (\w+ est une suite de caractères de mot)
m = re.match(r"\w+", text)
print(m.group()) # user_id
# search: première suite de chiffres n'importe où
s = re.search(r"\d+", text)
print(s.group()) # 12345
# findall: chaque suite de chiffres
nums = re.findall(r"\d+", text)
print(nums) # ['12345', '30']
Écris regex comme une raw string r"..."
Les antislashs apparaissent partout dans regex. Une chaîne normale "\d" peut voir ses échappements interprétés par la couche string avant que re ne la voie, donc il est plus sûr d'écrire la raw string `r"\d"` avec le r au début. Les éditeurs ont aussi tendance à mettre en évidence les raw strings comme regex, ce qui améliore la lisibilité.
Groupes de capture — Sortir des parties spécifiques d'un motif
Tout ce que tu mets entre `( )` dans un regex devient un groupe de capture — au lieu de juste le match complet, tu peux sortir chaque morceau séparément. Des motifs comme r"#(\d+) on (\d{4})-(\d{2})-(\d{2})" te permettent d'extraire un numéro de commande et une date d'une ligne de log d'un coup.
Appelle `.group(N)` sur le Match object pour lire le N-ième groupe (numéroté à partir de 1). .group(0) (ou .group() sans argument) retourne le match complet.
.group(1) / .group(2). .group(0) est le match complet.import re
text = "Order #1234 placed on 2024-03-15"
# Ce que veut dire le motif :
# # → '#' littéral
# (\d+) → un ou plusieurs chiffres → group(1) numéro de commande
# placed on → 'placed on' littéral
# (\d{4}) → 4 chiffres → group(2) année
# (\d{2}) → 2 chiffres → group(3) mois
# (\d{2}) → 2 chiffres → group(4) jour
m = re.search(r"#(\d+) placed on (\d{4})-(\d{2})-(\d{2})", text)
if m:
print("whole:", m.group(0)) # #1234 placed on 2024-03-15
print("order #:", m.group(1)) # 1234
print("year:", m.group(2)) # 2024
print("month:", m.group(3)) # 03
print("day:", m.group(4)) # 15
Appeler .group() quand Match est None lève une erreur
Quand re.search ne trouve pas le motif il retourne None. Appeler m.group() dessus crashe avec AttributeError: 'NoneType' object has no attribute 'group'. Vérifie toujours avec `if m:` d'abord avant .group(), ou fais les deux en une étape avec l'opérateur morse : if m := re.search(...): ....
re.sub — Remplacer des correspondances de motif
« Masquer des données personnelles d'un log », « enlever les balises HTML et garder le texte du corps », « normaliser un mélange d'espaces pleine largeur et demi-largeur » — ils reviennent tous à « réécrire tout ce qui correspond à un motif en autre chose ». Le replace de string ne gère que des sous-chaînes fixes, mais re.sub le fait par motif.
`re.sub(motif, remplacement, original)` retourne une nouvelle chaîne avec chaque correspondance remplacée par le remplacement. La chaîne originale est inchangée (les chaînes Python sont immuables, donc tu travailles toujours avec la valeur de retour).
import re
# Masquer les chiffres dans un numéro de téléphone (remplacer chaque \d par un *)
text = "Tel: 03-1234-5678"
masked = re.sub(r"\d", "*", text)
print(masked)
# Tel: **-****-****
# Enlever les balises HTML pour garder seulement le texte du corps
html = "<p>Bonjour <b>monde</b></p>"
plain = re.sub(r"<[^>]+>", "", html)
print(plain)
# Bonjour monde
re.compile — Réutiliser un motif
Quand tu utilises le même regex à répétition, écrire re.search(r"...", text) encore et encore fait que le moteur parse (compile) le motif à chaque fois, ce qui est du travail inutile. `re.compile(motif)` construit un objet motif compilé une seule fois, et tu appelles des méthodes dessus comme pattern.search(...) / pattern.findall(...) / pattern.sub(...). Le code se lit mieux et tourne plus vite.
.search / .findall / .sub autant de fois que nécessaire. Compile quand tu réutilises le même motif.import re
# Réutiliser le même motif de numéro de téléphone
phone_re = re.compile(r"\d{2,4}-\d{4}-\d{4}")
print(phone_re.findall("03-1234-5678 ou 080-1111-2222"))
# ['03-1234-5678', '080-1111-2222']
print(phone_re.search("mon téléphone est 03-9999-0000").group())
# 03-9999-0000
print(phone_re.sub("<phone>", "Contact: 03-1234-5678"))
# Contact: <phone>
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Quel est le bon regex pour un ou plusieurs chiffres à la suite ?
Question 3À partir de re.search(r"(\w+)@(\w+)", "alice@example"), quel appel retourne juste le domaine ?
Question 4Quelle est la principale raison d'utiliser une raw string `r"..."` quand on écrit du regex en Python ?