05 - Groupes et alternation : structurer tes patterns
Ce que tu vas apprendre
- Les groupes capturants
()et comment récupérer les captures - L'alternation
|pour dire "ceci ou cela" - Les groupes non-capturants
(?:)et quand les utiliser - Les groupes imbriques et la numerotation
- Un exemple concret : parser des dates
Prerequisites
Maitriser les classes de caractères et les quantificateurs.
Le problème : capturer des morceaux
Jusqu'ici, tes regex matchent du texte, mais tu ne peux pas extraire les morceaux qui t'interessent. Tu matche une date, mais tu ne peux pas récupérer le jour, le mois et l'annee séparément.
Les groupes resolvent ca. Ils permettent de delimiter des sous-parties de ta regex et de capturer le texte correspondant.
Les parentheses `()` : groupes capturants
Entoure une partie de ta regex avec des parentheses pour créer un groupe capturant :
(\d{2})/(\d{2})/(\d{4})
Sur le texte "28/03/2026" :
- Match complet : "28/03/2026"
- Groupe 1 : "28" (le jour)
- Groupe 2 : "03" (le mois)
- Groupe 3 : "2026" (l'annee)
Sur regex101, tu peux voir les captures dans le panneau de droite sous "Match Information". C'est la que tu realises la puissance des groupes.
En JavaScript, tu recuperes les captures comme ca :
javascriptconst match = "28/03/2026".match(/(\d{2})\/(\d{2})\/(\d{4})/);
// match[0] = "28/03/2026" (match complet)
// match[1] = "28" (groupe 1)
// match[2] = "03" (groupe 2)
// match[3] = "2026" (groupe 3)
En Python :
pythonimport re
m = re.match(r"(\d{2})/(\d{2})/(\d{4})", "28/03/2026")
# m.group(1) = "28"
# m.group(2) = "03"
# m.group(3) = "2026"
L'alternation `|` : le OU logique
Le pipe | fonctionne comme un OU. Il matche ce qui est a gauche OU ce qui est a droite :
chat|chien
Matche "chat" ou "chien".
Teste avec :
j'ai un chat
j'ai un chien
j'ai un hamster
L'alternation s'applique a tout ce qui est de chaque cote du |. Parfois, c'est trop large. Regarde :
bon|mauvais jour
Ca matche "bon" OU "mauvais jour". Pas "bon jour" ou "mauvais jour". Le | coupe la regex en deux au niveau le plus haut.
Grouper pour limiter l'alternation
C'est la que les parentheses jouent un double rôle. En plus de capturer, elles delimitent la portee de l'alternation :
(bon|mauvais) jour
Maintenant ca matche "bon jour" ou "mauvais jour". Les parentheses limitent le | au groupe.
Autre exemple, matcher des extensions de fichiers :
\.(js|ts|jsx|tsx)$
Matche les fichiers qui se terminent par .js, .ts, .jsx ou .tsx. L'alternation est limitee au groupe, et le $ s'applique apres.
Grouper pour quantifier
Les parentheses permettent aussi d'appliquer un quantificateur a une sequence de caractères :
(ha)+
Matche "ha", "haha", "hahaha"... Le + s'applique au groupe entier "ha", pas juste au "a".
Sans les parentheses, ha+ matcherait "ha", "haa", "haaa"... (le + sur le "a" seul).
Autre exemple :
(\d{2}\.){3}\d{2}
Matche un format comme "12.34.56.78" (trois groupes "chiffres-point" suivis de deux chiffres). C'est le pattern de base pour une adresse IP (meme si une vraie validation d'IP est plus complexe).
Les groupes non-capturants `(?:)`
Parfois tu veux grouper sans capturer. Par exemple, tu utilises des parentheses juste pour limiter l'alternation, mais tu n'as pas besoin de récupérer le contenu.
(?:bon|mauvais) jour
Le (?:) créé un groupe qui ne capture rien. Pourquoi s'embeter ? Deux raisons :
Performance : sur de gros textes avec des milliers de matchs, les captures inutiles ont un coût. Minime, mais réel.
Clarte : quand tu as des groupes capturants et non-capturants melanges, la numerotation des captures reste propre. C'est surtout ca qui compte en pratique.
Exemple avec un mix :
(?:https?://)?([\w.-]+)/(\w+)
(?:https?://)?: le protocole, optionnel, non-capture([\w.-]+): groupe 1, le domaine(\w+): groupe 2, le chemin
Si tu avais utilise des parentheses normales pour le protocole, le domaine serait le groupe 2 et le chemin le groupe 3. Ca decale tout.
Groupes imbriques
Tu peux imbriquer des groupes. La numerotation suit l'ordre des parentheses ouvrantes, de gauche a droite :
((a)(b(c)))
Sur le texte "abc" :
- Groupe 1 : "abc" (première parenthese ouvrante)
- Groupe 2 : "a" (deuxieme parenthese ouvrante)
- Groupe 3 : "bc" (troisieme parenthese ouvrante)
- Groupe 4 : "c" (quatrieme parenthese ouvrante)
C'est logique une fois que tu as compris la regle, mais ca peut devenir vite confus avec beaucoup de niveaux. Mon conseil : si tu depasses deux niveaux d'imbrication, repense ta regex.
Exemple complet : parser un log
Tu as des lignes de log comme :
[2026-03-28 14:30:15] ERROR: Connection timeout
[2026-03-28 14:30:16] INFO: Retry attempt 1
[2026-03-28 14:30:17] WARN: Slow query detected
Tu veux extraire la date, l'heure, le niveau et le message :
\[(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})\] (\w+): (.+)
- Groupe 1 : "2026-03-28" (date)
- Groupe 2 : "14:30:15" (heure)
- Groupe 3 : "ERROR" (niveau)
- Groupe 4 : "Connection timeout" (message)
Sur paltemps.fr, on utilise ce genre de regex dans nos scripts de monitoring pour extraire les erreurs des logs de production. Ca fait le boulot sans devoir installer un parser specialise.
Exemple complet : parser et reformater une date
Tu recois des dates au format français "JJ/MM/AAAA" et tu veux les convertir en format ISO "AAAA-MM-JJ" :
Pattern de recherche :
(\d{2})/(\d{2})/(\d{4})
Pattern de remplacement :
$3-$2-$1
"28/03/2026" devient "2026-03-28". Les $1, $2, $3 dans le remplacement font référencé aux groupes captures. On approfondira ca dans l'article suivant sur les backreferences.
Erreur courante : trop de groupes capturants
Je vois souvent des débutants mettre des parentheses partout "au cas ou". Chaque groupe capturant a un coût en lisibilité et en complexité. Utilise (?:) quand tu n'as pas besoin de la capture. Ta regex sera plus facile a comprendre et les numeros de groupes resteront coherents.
Résumé
()créé un groupe capturant qui extrait le texte matche|est l'alternation : matche le cote gauche OU le cote droit- Les parentheses limitent la portee de
|et permettent de quantifier des sequences (?:)créé un groupe non-capturant pour grouper sans capturer- Les groupes imbriques sont numerotes par ordre de parenthese ouvrante
- Les captures sont accessibles via
$1,$2(remplacement) oumatch[1],match[2](code)
| Article précédent | 04 - Classes de caractères |
| Article suivant | 06 - Backreferences |