02 - Quantificateurs : combien de fois tu veux matcher
Ce que tu vas apprendre
- Les quantificateurs
*,+,?,{n},{n,},{n,m} - La différence entre greedy (gourmand) et lazy (paresseux)
- Pourquoi
.*est souvent un piège - Des exemples pratiques de chaque quantificateur
Prerequisites
Avoir compris les caractères litteraux et l'echappement.
Le problème : matcher une quantité variable
Jusqu'ici, chaque caractère dans ta regex correspond a exactement un caractère dans le texte. Mais dans la vraie vie, tu ne sais pas toujours combien de fois un caractère apparaît. Un numero de telephone peut avoir 10 ou 12 chiffres. Un mot peut faire 2 ou 15 lettres. Un espace peut etre simple ou triple.
C'est la que les quantificateurs entrent en scene. Ils repondent a la question : "combien de fois le caractère précédent peut-il apparaître ?"
L'etoile `*` : zero ou plus
Le quantificateur * signifie "zero ou plusieurs fois". Le caractère qui le precede peut etre absent ou répété autant de fois que nécessaire.
ab*c
Matche : "ac" (zero b), "abc" (un b), "abbc" (deux b), "abbbbbc" (plein de b).
Teste sur regex101 avec ce texte :
ac
abc
abbc
abbbbc
adc
Le * est le plus permissif des quantificateurs. Il accepte meme l'absence totale du caractère. C'est a la fois sa force et son danger.
Le plus `+` : un ou plus
Le + est comme *, mais il exige au moins une occurrence.
ab+c
Matche : "abc", "abbc", "abbbbbc". Mais PAS "ac" (il faut au moins un b).
En pratique, + est souvent plus utile que *. Quand tu cherches des chiffres, tu veux au moins un chiffre, pas zero chiffre. Quand tu cherches des espaces, tu veux au moins un espace.
\d+
Matche un ou plusieurs chiffres : "42", "7", "12345". C'est probablement la regex la plus utile que tu apprendras aujourd'hui.
Le point d'interrogation `?` : zero ou un
Le ? rend le caractère précédent optionnel. Il peut etre là où pas, mais pas plus d'une fois.
colou?r
Matche : "color" (zero u) et "colour" (un u). Mais pas "colouur".
C'est parfait pour gerer les variantes orthographiques :
https?://
Matche "http://" et "https://". Le s est optionnel.
Les accolades `{n}` : exactement n fois
Quand tu sais exactement combien de repetitions tu veux :
\d{4}
Matche exactement quatre chiffres : "2026", "1234", "0000".
Teste avec :
42
123
2026
12345
Sur "12345", la regex matche "1234" (les quatre premiers chiffres). Elle ne vérifié pas qu'il n'y a rien apres. On verra comment faire ca avec les ancres.
Les accolades `{n,}` : n ou plus
\d{2,}
Matche deux chiffres ou plus : "42", "123", "999999". Mais pas "7" (un seul chiffre).
Les accolades `{n,m}` : entre n et m
\d{2,4}
Matche entre deux et quatre chiffres : "42", "123", "2026". Mais pas "7" (trop court) et sur "12345" ne matche que "1234" (s'arrêté a quatre).
Voici un tableau recapitulatif :
| Quantificateur | Signification | Exemple | Matche |
|---|---|---|---|
* |
0 ou plus | ab*c |
ac, abc, abbc... |
+ |
1 ou plus | ab+c |
abc, abbc... (pas ac) |
? |
0 ou 1 | ab?c |
ac, abc (pas abbc) |
{3} |
exactement 3 | a{3} |
aaa |
{2,} |
2 ou plus | a{2,} |
aa, aaa, aaaa... |
{2,4} |
entre 2 et 4 | a{2,4} |
aa, aaa, aaaa |
Greedy vs Lazy : le piège classique
Par défaut, tous les quantificateurs sont greedy (gourmands). Ils matchent le maximum de caractères possible. C'est la source de bugs subtils.
Prenons un exemple. Tu veux extraire le contenu entre guillemets dans cette chaîne :
Il a dit "bonjour" puis "au revoir"
Tu ecris la regex :
".*"
Tu t'attends a matcher "bonjour" et "au revoir" séparément. Mais non. Le .* etant gourmand, il matche tout d'un coup : "bonjour" puis "au revoir". Il prend le premier guillemet et va chercher le dernier, en avalant tout ce qu'il y a entre les deux.
C'est le piège numero un des regex. Et je suis tombe dedans plus d'une fois.
La solution : le mode lazy
En ajoutant un ? apres n'importe quel quantificateur, tu le rends lazy (paresseux). Au lieu de matcher le maximum, il matche le minimum.
".*?"
Maintenant sur Il a dit "bonjour" puis "au revoir", tu obtiens deux matchs separes : "bonjour" et "au revoir". Le .*? s'arrêté au premier guillemet fermant qu'il trouve.
Voici les equivalences :
| Greedy | Lazy | Comportement lazy |
|---|---|---|
* |
*? |
0 ou plus, minimum |
+ |
+? |
1 ou plus, minimum |
? |
?? |
0 ou 1, préféré 0 |
{n,m} |
{n,m}? |
entre n et m, préféré n |
Exemple concret : extraire des balises HTML
Tu as ce HTML :
html<b>gras</b> et <i>italique</i>
Regex greedy :
<.*>
Matche tout d'un bloc : <b>gras</b> et <i>italique</i>. Le .* traverse les balises fermantes pour aller jusqu'au dernier >.
Regex lazy :
<.*?>
Matche chaque balise individuellement : <b>, </b>, <i>, </i>. Beaucoup plus utile.
Sur paltemps.fr, on utilise fréquemment le mode lazy pour parser des fragments de texte structures. C'est devenu un reflexe.
L'alternative au lazy : la classe negee
Plutot que ".*?", tu peux aussi écrire :
"[^"]*"
Ca se lit : un guillemet, puis zero ou plusieurs caractères qui ne sont PAS un guillemet, puis un guillemet. On verra les classes de caractères en détail dans l'article 04, mais retiens que cette approche est souvent plus performante que le lazy.
Erreur courante : `.*` sans contrainte
La regex la plus dangereuse du monde :
.*
Elle matche tout. Absolument tout (sauf les retours a la ligne). C'est rarement ce que tu veux. Si tu te retrouves a écrire .*, demande-toi toujours : "est-ce que je peux etre plus precis ?"
Au lieu de .*, préféré :
\d+pour des chiffres\w+pour des caractères de mot[^"]+pour "tout sauf un guillemet".+?si tu veux vraiment "n'importe quoi" mais en mode lazy
Résumé
*matche 0 ou plus,+matche 1 ou plus,?matche 0 ou 1{n},{n,},{n,m}permettent un contrôle precis du nombre de repetitions- Par défaut, les quantificateurs sont greedy (prennent le maximum)
- Ajouter
?apres un quantificateur le rend lazy (prend le minimum) .*est presque toujours une mauvaise idee sans contrainte- Prefere les classes negees (
[^X]*) au lazy quand c'est possible
| Article précédent | 01 - Les bases |
| Article suivant | 03 - Ancres |