J'ai trouvé ceci excellent tutoriel sur les expressions régulières et bien que je comprenne intuitivement ce que font les quantificateurs "glouton", "réticent" et "possessif", il semble y avoir un sérieux trou dans ma compréhension.
Plus précisément, dans l'exemple suivant:
Enter your regex: .*foo // greedy quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfooxxxxxxfoo" starting at index 0 and ending at index 13.
Enter your regex: .*?foo // reluctant quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfoo" starting at index 0 and ending at index 4.
I found the text "xxxxxxfoo" starting at index 4 and ending at index 13.
Enter your regex: .*+foo // possessive quantifier
Enter input string to search: xfooxxxxxxfoo
No match found.
L'explication mentionne en mangeant toute la chaîne de saisie, les lettres ayant été consommées , matcher en reculant , l'occurrence la plus à droite de "foo" a été régurgitée , etc. .
Malheureusement, malgré les métaphores de Nice, je ne comprends toujours pas ce qui est mangé par qui ... Connaissez-vous un autre tutoriel qui explique (de manière concise) comment fonctionne les moteurs d'expressions régulières?
Alternativement, si quelqu'un peut expliquer le paragraphe suivant de manière légèrement différente, ce serait très apprécié:
Le premier exemple utilise le quantificateur glouton. * Pour trouver "quelque chose", zéro fois ou plus, suivi des lettres "f" "o" "o". Étant donné que le quantificateur est glouton, la partie. * De l'expression mange d'abord la chaîne d'entrée en entier. A ce stade, l'expression globale ne peut pas réussir, car les trois dernières lettres ("f" "o" "o") ont déjà été consommées ( par qui? ). Donc, le matcher recule lentement ( de droite à gauche? ) lettre par lettre jusqu'à ce que l'occurrence la plus à droite de "foo" ait été régurgitée ( qu'est-ce que cela signifie? ), date à laquelle la correspondance est réussie et la recherche terminée.
Le deuxième exemple, cependant, est réticent, il commence donc par consommer ( par qui? ) "rien". Parce que "foo" n'apparaît pas au début de la chaîne, il est obligé d'avaler ( qui avale?) La première lettre (un "x" ), qui déclenche la première correspondance à 0 et 4. Notre faisceau de test poursuit le processus jusqu'à épuisement de la chaîne d'entrée. Il trouve un autre match à 4 et 13.
Le troisième exemple ne parvient pas à trouver une correspondance car le quantificateur est possessif. Dans ce cas, toute la chaîne d'entrée est consommée par. * +, ( comment? ) ne laissant rien pour satisfaire le "foo" à la fin de l'expression. Utilisez un quantificateur possessif pour les situations dans lesquelles vous souhaitez tout saisir sans jamais reculer ( que signifie "reculer"? ); il surperformera le quantificateur glouton équivalent dans les cas où la correspondance n'est pas immédiatement trouvée.
Je vais tenter le coup.
Un avide premier quantificateur correspond le plus possible. Donc, le .*
correspond à la chaîne entière. Ensuite, le matcher essaie de faire correspondre la f
suivante, mais il ne reste plus aucun caractère. Cela revient donc à "revenir en arrière", faisant que le quantificateur glouton corresponde à une seule chose en moins (laissant le "o" à la fin de la chaîne inégalée). Cela ne correspond toujours pas à la f
de la regex, donc elle "revient en arrière" une étape supplémentaire, faisant en sorte que le quantificateur glouton corresponde à une chose de moins (en laissant le "oo" à la fin de la chaîne inégalée). Cela toujours ne correspond pas à la f
de la regex, donc il revient en arrière d'un pas de plus (en laissant le "foo" à la fin de la chaîne inégalée). Maintenant, le correcteur correspond enfin à la f
de la regex, et la o
et la suivante o
correspondent également. Succès!
Un premier quantificateur réticent ou "non-gourmand" correspond le moins possible. Donc, le .*
ne correspond à rien au début, laissant toute la chaîne sans correspondance. Ensuite, le matcher essaie de faire correspondre la suite f
, mais la partie non appariée de la chaîne commence par "x", de sorte que cela ne fonctionne pas. Donc, le correcteur fait un retour en arrière, faisant correspondre le quantificateur non gourmand à une dernière chose (maintenant, il correspond au "x", laissant "fooxxxxxxfoo" inégalé). Ensuite, il essaie de faire correspondre la f
, qui réussit, ainsi que la o
et la suivante o
dans le match regex. Succès!
Dans votre exemple, il recommence ensuite le processus avec la partie non appariée restante de la chaîne, en suivant le même processus.
Un quantificateur possessif est semblable au quantificateur glouton, mais il ne fait pas retour en arrière. Donc, il commence avec .*
correspondant à la chaîne entière, ne laissant rien inégalé. Il ne lui reste alors plus rien à faire avec le f
de la regex. Puisque le quantificateur possessif ne fait pas retour en arrière, la correspondance échoue là.
Je n'ai jamais entendu les termes exacts "régurgiter" ou "reculer"; la phrase qui les remplacerait est "revenir en arrière", mais "régurgiter" semble être une phrase aussi valable que toute phrase pour "le contenu qui avait été accepté provisoirement avant de revenir en arrière l'a jeté à nouveau".
La chose importante à comprendre à propos de la plupart des moteurs regex est qu’ils font un retour arrière : ils vont provisoirement accepte une correspondance potentielle partielle en essayant de faire correspondre tout le contenu de la regex. Si la regex ne peut pas être complètement appariée à la première tentative, le moteur de regex va revenir en arrière sur l'un de ses matchs. Il essaiera de faire correspondre les répétitions *
, +
, ?
, en alternance ou {n,m}
différemment, puis réessayez. (Et oui, ce processus peut prendre beaucoup de temps.)
Le premier exemple utilise le quantificateur glouton. * Pour trouver "quelque chose", zéro fois ou plus, suivi des lettres "f" "o" "o". Étant donné que le quantificateur est glouton, la partie. * De l'expression mange d'abord la chaîne d'entrée en entier. A ce stade, l'expression globale ne peut pas réussir car les trois dernières lettres ("f" "o" "o") ont déjà été consommées (par qui?).
Les trois dernières lettres, f
, o
et o
étaient déjà utilisées par la partie initiale .*
de la règle. Cependant, l'élément suivant de l'expression rationnelle, f
, n'a plus rien dans la chaîne d'entrée. Le moteur sera obligé de revenir en arrière sur sa correspondance initiale .*
et d'essayer de faire correspondre le dernier caractère, sauf le dernier. (Cela pourrait être intelligent et revenir en arrière sauf les trois derniers, car il comporte trois termes littéraux, mais je ne suis pas au courant de la mise en œuvre. détails à ce niveau.)
Donc, le matcher recule lentement (de droite à gauche?) lettre par lettre jusqu'à ce que l'occurrence la plus à droite de "foo" ait été régurgitée (qu'est-ce que cela signifie? =), à laquelle
Cela signifie que la foo
avait provisoirement été incluse lors de la mise en correspondance de .*
. Comme cette tentative a échoué, le moteur des expressions rationnelles tente d'accepter un caractère de moins dans .*
. S'il y avait eu une correspondance réussie avant le .*
dans cet exemple, le moteur essaierait probablement de raccourcir la correspondance .*
(de droite à gauche, comme vous l'avez fait remarquer, car il s'agit d'un qualificatif gourmand), et s'il ne parvenait pas à faire correspondre toutes les entrées, il pourrait alors être obligé de réévaluer ce à quoi il correspondait avant le .*
dans mon exemple hypothétique.
pointez le match et la recherche se termine.
Le deuxième exemple, cependant, est réticent. Il commence donc par consommer (par qui?) "rien". Parce que "foo"
Le rien initial est consommé par .?*
, qui consommera la quantité la plus courte possible de tout ce qui permet au reste de l'expression rationnelle de correspondre.
n'apparaît pas au début de la chaîne, il est obligé d'avaler (who avale?) le
Encore une fois, le .?*
utilise le premier caractère, après un retour en arrière sur l'échec initial pour faire correspondre la regex entière avec la correspondance la plus courte possible. (Dans ce cas, le moteur des expressions rationnelles étend la correspondance pour .*?
de gauche à droite, car .*?
est réticent.)
première lettre (un "x"), qui déclenche la première correspondance à 0 et 4. Notre test continue le processus jusqu'à épuisement de la chaîne d'entrée. Il trouve un autre match à 4 et 13.
Le troisième exemple ne parvient pas à trouver une correspondance car le quantificateur est possessif. Dans ce cas, toute la chaîne d'entrée est consommée par. * +, (comment?)
Un .*+
consomme le plus possible et ne revient pas en arrière pour rechercher de nouveaux correspondances lorsque l'ensemble des expressions rationnelles ne parvient pas à trouver une correspondance. . Parce que la forme possessive n'effectue pas de retour en arrière, vous ne verrez probablement pas beaucoup d'utilisations avec .*+
, mais plutôt avec des classes de caractères ou des restrictions similaires: account: [[:digit:]]*+ phone: [[:digit:]]*+
.
Cela peut considérablement accélérer la correspondance des expressions rationnelles, car vous dites au moteur des expressions rationnelles qu'il ne doit jamais revenir en arrière sur les correspondances potentielles si une entrée ne correspond pas. (Si vous deviez écrire tout le code correspondant à la main, cela ressemblerait à ne jamais utiliser putc(3)
pour "repousser" un caractère saisi. Ce serait très similaire au code naïf que l'on pourrait écrire du premier coup Sauf que les moteurs de regex sont bien meilleurs qu'un seul caractère de Push-back, ils peuvent revenir en arrière à zéro et essayer à nouveau. :)
Mais plus que des accélérations potentielles, cela peut également vous permettre d’écrire des expressions rationnelles qui correspondent exactement à ce que vous devez faire correspondre. J'ai du mal à trouver un exemple simple :) mais écrire une regex en utilisant des quantificateurs possessifs vs gloutons peut vous donner des correspondances différentes, et l'un ou l'autre peut être plus approprié.
ne laissant rien pour satisfaire le "foo" à la fin de l'expression. Utilisez un quantificateur possessif pour les situations dans lesquelles vous souhaitez tout saisir sans jamais reculer (que veut dire reculer?); il va surperformer
"Reculer" dans ce contexte signifie "revenir en arrière" - abandonner une correspondance partielle provisoire pour essayer une autre correspondance partielle, qui peut ou non aboutir.
le quantificateur glouton équivalent dans les cas où la correspondance n'est pas immédiatement trouvée.
http://swtch.com/~rsc/regexp/regexp1.html
Je ne suis pas sûr que ce soit la meilleure explication sur Internet, mais elle est assez bien écrite et suffisamment détaillée, et j'y reviens sans cesse. Vous voudrez peut-être y jeter un coup d'œil.
Si vous souhaitez une explication de niveau supérieur (explication moins détaillée), pour les expressions rationnelles simples telles que celle que vous consultez, un moteur d’expression régulière fonctionne par retour en arrière. Essentiellement, il choisit ("mange") une section de la chaîne et essaie de faire correspondre l'expression régulière à cette section. Si cela correspond, génial. Sinon, le moteur modifie le choix de la section de la chaîne et essaie de faire correspondre l'expression rationnelle à cette section, et ainsi de suite, jusqu'à ce que tous les choix possibles soient essayés.
Ce processus est utilisé de manière récursive: dans sa tentative de correspondance d'une chaîne avec une expression régulière donnée, le moteur scinde l'expression régulière en morceaux et applique l'algorithme individuellement à chaque morceau.
La différence entre les quantificateurs gourmands, réticents et possessifs intervient lorsque le moteur choisit la partie de la chaîne à essayer, et comment modifier ce choix s'il ne fonctionne pas la première fois. Les règles sont les suivantes:
Un quantificateur glouton indique au moteur de démarrer avec la chaîne entière (ou du moins, la totalité de celle-ci qui n'a pas déjà été comparée par les parties précédentes de la expression régulière) et vérifiez si elle correspond à l’expression rationnelle. Si c'est le cas, génial; le moteur peut continuer avec le reste de l'expression rationnelle. Si ce n'est pas le cas, il essaie à nouveau, mais élimine un caractère (le dernier) de la section de la chaîne à vérifier. Si cela ne fonctionne pas, cela élimine un autre personnage, etc. Ainsi, un quantificateur glouton vérifie les correspondances possibles, du plus long au plus court.
Un quantificateur réticent indique au moteur de démarrer avec le morceau le plus court possible de la chaîne. Si cela correspond, le moteur peut continuer; sinon, il ajoute un caractère à la section de la chaîne vérifiée et l'essaie, et ainsi de suite jusqu'à ce qu'il trouve une correspondance ou que la chaîne entière ait été épuisé. Ainsi, un quantificateur réticent vérifie les correspondances possibles du plus court au plus long.
Un quantificateur possessif est comme un quantificateur glouton au premier essai: il indique au moteur de commencer en vérifiant toute la chaîne. La différence est que si cela ne fonctionne pas, le quantificateur possessif signale que la correspondance a échoué sur-le-champ. Le moteur ne change pas la section de la chaîne examinée et ne fait plus aucune tentative.
C’est pourquoi la correspondance avec le quantificateur possessif échoue dans votre exemple: le .*+
est vérifié par rapport à la chaîne entière, à laquelle il correspond, puis le moteur cherche ensuite des caractères supplémentaires foo
après cela - mais de Bien sûr, il ne les trouve pas, car vous êtes déjà à la fin de la chaîne. S'il s'agissait d'un quantificateur glouton, il ferait un retour en arrière et tenterait de faire en sorte que le .*
ne corresponde qu'au dernier caractère, puis au troisième au dernier caractère, puis au quatrième et dernier caractère, qui réussit car il ne reste alors que foo
après que le .*
a "mangé" la partie antérieure de la chaîne.
Voici ma prise en utilisant les positions de cellule et d'index (voir le diagramme ici pour distinguer une cellule d'un index).
Greedy - Match autant que possible au quantificateur glouton et à la regex entière. S'il n'y a pas de correspondance, revenez sur le quantificateur glouton.
Chaîne d'entrée: xfooxxxxxxfoo
Regex: . * Foo
La regex ci-dessus a deux parties:
(moi et
ii) "foo"
Chacune des étapes ci-dessous analysera les deux parties. Les commentaires supplémentaires pour une correspondance avec 'Pass' ou 'Fail' sont expliqués entre accolades.
Étape 1:
(i). * = xfooxxxxxxfoo - PASS ('. *' est un quantificateur glouton et utilisera toute la chaîne d'entrée)
(ii) foo = Il ne reste plus aucun caractère à faire correspondre après l'index 13 - ECHEC
La correspondance a échoué.
Étape 2:
(i). * = xfooxxxxxxfo - PASS (retour sur le quantificateur glouton '. *')
(ii) foo = o - FAIL
La correspondance a échoué.
Étape 3:
(i). * = xfooxxxxxxf - PASS (retour sur le quantificateur glouton '. *')
(ii) foo = oo - FAIL
La correspondance a échoué.
Étape 4:
(i). * = xfooxxxxxx - PASS (retour sur le quantificateur glouton '. *')
(ii) foo = foo - PASS
Rapport MATCH
Résultat: 1 match (es)
J'ai trouvé le texte "xfooxxxxxxfoo" commençant à l'index 0 et se terminant à l'index 13.
Réluctant - Associez le moins possible au quantificateur réluctant et à l'intégralité de l'expression régulière. s'il n'y a pas de correspondance, ajoutez des caractères au quantificateur à réticence.
Chaîne d'entrée: xfooxxxxxxfoo
Regex: . *? Foo
La regex ci-dessus a deux parties:
(je) '.*?' et
ii) "foo"
Étape 1:
. *? = '' (blanc) - PASS (correspond le moins possible au quantificateur à réticence '. *?'. L'index 0 ayant '' est une correspondance.)
foo = xfo - FAIL (Cellule 0,1,2 - i.e indice compris entre 0 et 3)
La correspondance a échoué.
Étape 2:
. *? = x - PASS (Ajoute des caractères au quantificateur de réticence '. *?'. La cellule 0 ayant 'x' est une correspondance.)
foo = foo - PASS
Rapport MATCH
Étape 3:
. *? = '' (blanc) - PASS (correspond le moins possible au quantificateur à réticence '. *?'. L'index 4 ayant le caractère '' est une correspondance.)
foo = xxx - FAIL (Cellule 4,5,6 - c'est-à-dire indice compris entre 4 et 7)
La correspondance a échoué.
Étape 4:
. *? = x - PASS (Ajoute des caractères au quantificateur à réticence '. *?'. Cellule 4.)
foo = xxx - FAIL (Cellule 5,6,7 - soit un indice compris entre 5 et 8)
La correspondance a échoué.
Étape 5:
. *? = xx - PASS (Ajoute des caractères au quantificateur à réticence '. *?'. Cellules 4 à 5.)
foo = xxx - FAIL (Cell 6,7,8 - i.e indice compris entre 6 et 9)
La correspondance a échoué.
Étape 6:
. *? = xxx - PASS (Ajoute des caractères au quantificateur à réticence '. *?'. Cell 4 à 6.)
foo = xxx - FAIL (Cell 7,8,9 - i.e indice compris entre 7 et 10)
La correspondance a échoué.
Étape 7:
. *? = xxxx - PASS (Ajoute des caractères au quantificateur de réticence '. *?'. Cell 4 à 7.)
foo = xxf - FAIL (Cellule 8,9,10 - soit un indice compris entre 8 et 11)
La correspondance a échoué.
Étape 8:
. *? = xxxxx - PASS (Ajoute des caractères au quantificateur de réticence '. *?'. Cell 4 à 8.)
foo = xfo - FAIL (Cellule 9,10,11 - soit un indice compris entre 9 et 12)
La correspondance a échoué.
Étape 9:
. *? = xxxxxx - PASS (Ajoute des caractères au quantificateur de réticence '. *?'. Cellules 4 à 9.)
foo = foo - PASS (Cellule 10,11,12 - soit un indice compris entre 10 et 13)
Rapport MATCH
Étape 10:
. *? = '' (vide) - PASS (correspond le moins possible au quantificateur à réticence '. *?'. L'index 13 est vide.)
foo = Il ne reste plus aucun caractère à faire correspondre - ECHEC (aucun élément ne correspond après l'index 13)
La correspondance a échoué.
Résultat: 2 match (es)
J'ai trouvé le texte "xfoo" commençant à l'index 0 et se terminant à l'index 4.
J'ai trouvé le texte "xxxxxxfoo" commençant à l'index 4 et se terminant à l'index 13.
Possessive - Associez autant que possible le quantifer possessif et le regex entier. NE PAS revenir en arrière.
Chaîne d'entrée: xfooxxxxxxfoo
Regex: . * + Foo
La regex ci-dessus a deux parties: '. * +' Et 'foo'.
Étape 1:
. * + = xfooxxxxxxfoo - PASS (Faites correspondre autant que possible le quantificateur possessif '. *')
foo = Il ne reste plus aucun caractère à faire correspondre - ÉCHEC (aucun résultat à faire après l'index 13)
La correspondance a échoué.
Remarque: Le retour en arrière n'est pas autorisé.
Résultat: 0 résultat (s)
Greedy Quantification implique la mise en correspondance de modèles en utilisant tous les caractères non validés restants d'une chaîne au cours d'une itération. Les caractères non validés commencent dans la séquence active . Chaque fois qu'une correspondance ne se produit pas, le caractère à la fin est mis en quarantaine et la vérification est effectuée à nouveau.
Lorsque seules les conditions principales du motif d'expression régulière sont satisfaites par la séquence active, une tentative est faite pour valider les conditions restantes par rapport à la quarantaine. Si cette validation réussit, les caractères correspondants de la quarantaine sont validés et les caractères non appariés restants restent non validés et seront utilisés lorsque le processus recommencera à la prochaine itération.
Le flux de caractères va de la séquence active à la quarantaine. Le comportement qui en résulte est que la plus grande partie possible de la séquence d'origine est incluse dans une correspondance.
Reluctant Quantification est généralement identique à une qualification gloutonne sauf que le flux de caractères est l'inverse - c'est-à-dire qu'ils commencent en quarantaine et s’écoulent dans la séquence active . Il en résulte que le moins possible de la séquence d'origine est incluse dans une correspondance.
Quantification Possessive n'a pas de quarantaine et inclut tout dans une séquence active fixe .
Greedy: "correspond à la plus longue séquence de caractères possible"
Réticent: "correspond à la séquence de caractères la plus courte possible"
Possessif: Ceci est un peu étrange car il ne cherche PAS (contrairement à gourmand et réticent) à trouver une correspondance pour la regex entière.
Soit dit en passant: aucune implémentation de matre de motifs regex n’utilisera jamais le retour en arrière. Tous les modèles de correspondance de la vie réelle sont extrêmement rapides - presque indépendants de la complexité de l'expression régulière!