Sur gourmand vs non gourmand
La répétition dans les regex par défaut est gourmande : ils essaient de faire correspondre autant de répétitions que possible, et quand cela ne fonctionne pas et qu'ils doivent revenir en arrière, ils essaient de faire correspondre une répétition de moins à la fois, jusqu'à ce qu'une correspondance du modèle entier soit a trouvé. En conséquence, lorsqu'un match se produit finalement, une répétition gourmande correspondrait à autant de répétitions que possible.
Le ?
quantificateur en tant que répétition change ce comportement en non gourmand , aussi appelé réticent ( par exemple en Java ) (et parfois «paresseux»). En revanche, cette répétition essaiera d'abord de faire correspondre le moins de répétitions possible, et lorsque cela ne fonctionne pas et qu'ils doivent revenir en arrière, ils commencent à faire correspondre un répétition de plus à la fois. En conséquence, lorsqu'un match se produit finalement, une répétition réticente correspondrait au moins de répétitions possible.
Références
Exemple 1: de A à Z
Comparons ces deux modèles: A.*Z
et A.*?Z
.
Compte tenu de l'entrée suivante:
eeeAiiZuuuuAoooZeeee
Les modèles donnent les correspondances suivantes:
Concentrons-nous d'abord sur ce que A.*Z
fait. Quand il correspond au premier A
, le .*
, étant gourmand, essaie d'abord d'en faire correspondre autant .
que possible.
eeeAiiZuuuuAoooZeeee
\_______________/
A.* matched, Z can't match
Puisque le Z
ne correspond pas, le moteur revient en arrière et .*
doit ensuite correspondre à un de moins .
:
eeeAiiZuuuuAoooZeeee
\______________/
A.* matched, Z still can't match
Cela se produit encore quelques fois, jusqu'à ce que nous en arrivions finalement à ceci:
eeeAiiZuuuuAoooZeeee
\__________/
A.* matched, Z can now match
Maintenant Z
peut correspondre, donc le modèle global correspond:
eeeAiiZuuuuAoooZeeee
\___________/
A.*Z matched
En revanche, la répétition réticente dans les A.*?Z
premiers matchs le moins .
possible, puis en prenant plus .
si nécessaire. Cela explique pourquoi il trouve deux correspondances dans l'entrée.
Voici une représentation visuelle de ce que les deux modèles correspondent:
eeeAiiZuuuuAoooZeeee
\__/r \___/r r = reluctant
\____g____/ g = greedy
Exemple: une alternative
Dans de nombreuses applications, les deux correspondances dans l'entrée ci-dessus sont ce qui est souhaité, ainsi un réticent .*?
est utilisé au lieu du gourmand .*
pour éviter un sur-appariement. Pour ce modèle particulier, cependant, il existe une meilleure alternative, en utilisant une classe de caractères inversée.
Le modèle A[^Z]*Z
trouve également les deux mêmes correspondances que le A.*?Z
modèle pour l'entrée ci-dessus ( comme vu sur ideone.com ). [^Z]
est ce qu'on appelle une classe de caractères annulée : elle correspond à tout sauf à Z
.
La principale différence entre les deux modèles réside dans les performances: étant plus stricte, la classe de caractères annulée ne peut correspondre qu'à une seule manière pour une entrée donnée. Peu importe si vous utilisez un modificateur gourmand ou réticent pour ce modèle. En fait, dans certaines saveurs, vous pouvez faire encore mieux et utiliser ce qu'on appelle le quantificateur possessif, qui ne revient pas du tout.
Références
Exemple 2: De A à ZZ
Cet exemple doit être illustratif: il montre comment les modèles de classe de caractères gourmands, réticents et annulés correspondent différemment avec la même entrée.
eeAiiZooAuuZZeeeZZfff
Voici les correspondances pour l'entrée ci-dessus:
Voici une représentation visuelle de ce à quoi ils correspondent:
___n
/ \ n = negated character class
eeAiiZooAuuZZeeeZZfff r = reluctant
\_________/r / g = greedy
\____________/g
Rubriques connexes
Ce sont des liens vers des questions et réponses sur stackoverflow qui couvrent certains sujets susceptibles de vous intéresser.
Une répétition gourmande peut en surpasser une autre