CodeKitHub
Français
Correspondance « gourmande » vs « paresseuse » en expressions régulières : pourquoi votre motif capture trop de caractères

Correspondance « gourmande » vs « paresseuse » en expressions régulières : pourquoi votre motif capture trop de caractères

Publié le 24 juil. 2026

Vous écrivez <b>.*</b> pour extraire le contenu d’une balise de gras, vous le testez sur <b>Hello</b>, et cela fonctionne parfaitement. Ensuite, vous l’exécutez sur du code HTML réel comportant deux balises de gras sur la même ligne — <b>Hello</b> and <b>World</b> — et au lieu d’obtenir deux correspondances, vous obtenez une seule correspondance géante couvrant les deux balises. Ce n’est pas un bug de votre moteur d’expressions régulières ; c’est le comportement par défaut de *, + et {n,m}, et il suffit d’un seul caractère pour y remédier une fois que vous comprenez ce qui se passe.

Pourquoi .* capture plus que prévu

Par défaut, les quantificateurs dans les expressions régulières sont gloutons.* ne signifie pas « trouver quelques caractères », mais « trouver autant de caractères que possible, puis ne reculer que si le reste du motif l’exige absolument ».

Examinons étape par étape la comparaison entre <b>.*</b> et <b>Hello</b> and <b>World</b>:

  1. <b> correspond au premier <b>.
  2. .* commence par consommer tout le reste de la chaîne — tout ce qui se trouve jusqu’à la fin, y compris le deuxième <b>World</b>.
  3. Le moteur doit ensuite trouver </b> pour terminer la correspondance ; il commence donc à remonter depuis la fin de .*, un caractère à la fois.
  4. Le premier emplacement (en parcourant la chaîne à rebours depuis la fin) où </b> s’insère est le tout dernier </b> de la chaîne — et non le premier où il devrait « logiquement » s’arrêter.

La correspondance finit donc par être <b>Hello</b> and <b>World</b> dans son intégralité, car la correspondance « gourmande » essaie toujours d’abord la chaîne la plus longue possible et ne la raccourcit que le strict minimum nécessaire pour que le reste du motif aboutisse.

La solution : rendre le quantificateur « paresseux » avec ?

L’ajout d’un ? immédiatement après un quantificateur le fait passer de « glouton » à « paresseux » (également appelé « non glouton » ou « réticent ») : *?, +?, ??, {n,m}?.

Un quantificateur « paresseux » fait l’inverse : il commence par faire correspondre le moins de caractères possible, puis ne s’étend que si le reste du motif ne peut pas encore aboutir.

<b>.*?</b> appliqué à la même chaîne :

  1. <b> correspond au premier <b>.
  2. .*? commence par faire correspondre zéro caractère.
  3. Le moteur vérifie : est-ce que </b> correspond à cet endroit précis ? Non (nous en sommes à « Hello… », pas encore à un </b>) — il étend donc .*? d’exactement un caractère et vérifie à nouveau.
  4. Ce processus se répète caractère par caractère jusqu’à ce que .*? ait consommé exactement Hello, moment auquel </b> correspond immédiatement.

Résultat : <b>Hello</b> et <b>World</b> sont renvoyés comme deux correspondances distinctes, ce qui correspond presque toujours à ce que l’on souhaite réellement lors de l’analyse de structures de type balises ou délimiteurs.

Quand vous souhaitez réellement utiliser l’approche gloutonne (ce n’est pas simplement « une mauvaise valeur par défaut »)

L’approche gloutonne n’est pas une erreur du langage — elle est correcte pour un cas différent, tout aussi courant : la correspondance avec la limite la plus externe d’un élément, et non avec la plus petite unité à l’intérieur de celui-ci. Si vous extrayez « tout ce qui se trouve entre le premier { et le dernier } » dans un bloc de texte de type JSON (par exemple, pour récupérer un objet entier indépendamment de l’imbrication), l’approche « greedy » est tout à fait appropriée, tandis que l’approche « lazy » s’arrêterait au premier } intérieur, vous donnant ainsi un fragment tronqué et invalide.

Règle générale : « lazy » pour les petits blocs délimités qui se répètent (balises, chaînes entre guillemets, éléments de liste) ; « greedy » pour « récupérer l’intégralité de la portée externe ».

Référence rapide

Motif Comportement À utiliser lorsque
.*, .+, {n,m} Greedy — correspond au plus long possible Vous voulez la portée la plus externe, ou s’il n’y a qu’une seule correspondance dans la chaîne
.*?, .+?, {n,m}? Paresseux — correspond à la plus courte possible Vous avez plusieurs segments délimités similaires et souhaitez les extraire séparément

Si vous n’êtes pas sûr du comportement réel d’un motif sur votre entrée réelle plutôt que sur votre chaîne de test, l’exécuter sur le texte multi-correspondance réel dans un testeur d’expressions régulières avec mise en évidence des correspondances en temps réel rendra la différence immédiatement visible — vous verrez un seul bloc géant mis en évidence pour le mode « greedy », contre plusieurs blocs distincts pour le mode « lazy », ce qui est généralement plus rapide que de raisonner caractère par caractère.

← Retour au blog