11
votes

Pourquoi mon non-gourmand Perl Regex correspond-il toujours trop?

dire, j'ai une ligne qui contient la chaîne suivante: xxx

et je veux extraire xxx

J'ai ce qui suit code: xxx

mais cela me donne plus que ce dont j'ai besoin: xxx

J'ai essayé de regrouper mon modèle dans son ensemble par Utilisation de la parens non capturant: xxx

mais le problème persiste.

J'ai relire la section Quantifictions Nongredy de l'apprentissage Perl, mais ça me dit nulle part Jusqu'à présent.

Merci pour toute guidage que vous pouvez généreusement offrir :)


3 commentaires

Le premier "Blah Blah Blash" n'est pas dans des guillemets. Votre REGEXP saisit donc le deuxième ensemble.


@ETher, mon problème est: Je pensais que Perl peut traiter mon modèle dans son ensemble. Mais je me trompais. La chose devient plus claire pour moi que Perl essaie toujours de faire correspondre le premier sous-modèle, puis la sous-cuisson suivante et sur et encore. Semble qu'il n'y ait rien de tel que "Match de modèle entier simultané".


@brian, merci. J'aime la façon dont vous avez reformulé ma question :)


4 Réponses :


19
votes

Le problème est que, même si ce n'est pas gourmand, cela continue d'essayer. La regex ne voit pas xxx

et pense "Oh, le contenu suivant le" dit "n'est pas cité, alors je vais sauter celui-là." Il pense "bien, le problème après" dit "n'est pas cité, il doit toujours faire partie de notre devis ." Donc ". +?" correspondent xxx

ce que vous voulez est "[^"] + ". Ceci va Faites correspondre deux marques de devis renfermant tout ce qui n'est pas une marque de devis. La solution finale: xxx


10 commentaires

Merci Chris, pour la solution et l'explication :) Merci beaucoup pour l'illumination!


++! Une autre bonne raison d'utiliser [^ "] + est qu'il va réduire le suivi arrière inutile et rendre votre regex plus efficace.


@DaoToad - J'en ai discuté avec Alex Martelli dans sa réponse. Ceci est souvent cité, mais même pour 10 000 000 comparaisons, la différence de performance est toujours presque imperceptible, à la fois en Perl et en Python. Il semble que le mantra change de "Le compilateur optimisera que" à "le moteur Regex l'optimise." : P (voir mon commentaire à sa réponse pour les résultats de mes timings.)


@Chris Lutz: Je pense que daoToad comparait "[^"] + " à ". * + ", pas sur " [^ "] +?" .


@ Chris, @ daotaod, @ ysth, j'ai une autre question. J'ai changé la chaîne en "" $ Tom ", dit Blah Blah Blash." $ Dick "dit" Blah Blah bla "." $ Harry "dit bla bla bla." $ Jane "dit" bla bla bla "." Ensuite, j'ai essayé "imprimer $ terme" mais je n'ai que "$ dick" dit "bla bla bla"? Pourquoi "" $ Jane "a déclaré" bla bla bla "n'est pas capturé? Merci!


J'ai déjà utilisé le modificateur G: "My ($ terme) = / (" [^ "] +" dit "[^"] + ") / g; impression $ Term \ n;" Mais les choses ne sont pas correctes. Qu'est-ce que je fais mal?


OkieDokie, le code suivant a fonctionné bien, mais je veux toujours savoir pourquoi "impression $ TERM" n'imprime que la première capture: "mon @group = ($ _ = ~ m / (" dit "[^" ] + ") / g); imprimer" @group \ n ";


Eh bien, j'ai essayé le code suivant et cela a fonctionné. Mais c'est assez gênant: mon ($ première, $ secondaire) = / ("[^"] + "dit" [^ "] +") / g; Imprimer $ Premier; Imprimer $ Deuxième;


Parce que mon ($ terme) = quelque chose attribue le retour du quelque chose (en tant que liste) à la liste ($ terme) et depuis cette liste A seulement un élément à attribuer à, Perl défausse silencieusement l'excédent. La bonne façon de capturer un nombre arbitraire de correspondances avec le modificateur / g est d'utiliser une liste comme @group afin qu'il a toujours suffisamment de place. Vous pouvez également utiliser le modificateur / g dans un pendant que en boucle à itérer via des correspondances, puis effectuez une action pour chacun. Ou vous pouvez utiliser mappe pour le même objectif: mappe {faire des choses} (/ regex / g);


@Chris, merci pour la clarification. J'ai déjà compris comment utiliser @group pour capturer plus d'un match, mais je n'ai tout simplement pas compris pourquoi ma construction ($ Term) a échoué. Maintenant je vois le point. C'est différent de $ _ = ~ construire. Merci :)



3
votes

Malheureusement, " code> est un caractère suffisant de manière particulière nécessaire à être traité avec soin. Utilisez:

my ($term) = /("[^"]+?" said "[^"]+?")/g;


3 commentaires

Woah! Le Seigneur de Python répond à une question Perl! Pincez-moi, je rêve. (En tant que note, le match non gourmand n'est plus nécessaire et il suffira probablement de ralentir le moteur de regex. Mais cela fonctionne, comme vous le dites.)


J'étais un excellent Perl'er de retour dans Perl 4 fois (et cette question est assez germane à Perl 4 ;-) - Ce sont les plus étrange nouveautés de Perl 5 qui me jetaient et a finalement fait une pythonistas de moi! -) Dans "Perl 4" fois, les non-gourmands n'ont jamais été moins efficaces que gourmands - n'ont regardé aucun actuel Perl RE moteurs pour voir s'ils ont gâché cela aussi! -) .


Il n'y a pas de différence extrême, mais après les tests, j'ai constaté que le gourmand <[^ "] +" est légèrement plus rapide que "[^"] +? ". Ce n'est pas une différence significative: 7,13 secondes pour la version gourmande, 7.68 pour le non-gourmand (pour 20 000 000 tests). Il est douteux que cet insignifiant d'une différence affecte jamais le code de la vie réelle, mais il est bon de noter. (Pour une exhaustivité, en Python, le gourmand prend 15,15 secondes et le non-gourmand prend 16,47 secondes. Même jeu de données.)



3
votes

D'autres ont mentionné comment résoudre ce problème.

Je vais répondre à la façon dont vous pouvez déboguer ceci: vous pouvez voir ce qui se passe en utilisant plus de captures: xxx


0 commentaires

2
votes

Votre problème ici est qu'il y a deux matchs possibles pour votre REGEXP, celui que vous voulez (un plus court) et celui du moteur Regex choisit. Le moteur choisit cette correspondance spécifique car elle préfère une correspondance qui commence plus tôt dans la chaîne et est plus longue à une correspondance qui commence plus tard et est plus courte. En d'autres termes, les premiers matches gagnent les plus courts.

Pour résoudre ce problème, vous devez rendre votre regex plus spécifique (comme en disant que le moteur ne doit contenir aucune citation. C'est une bonne idée de rendre vos expressions aussi précises que possible.

Pour plus de détails et de gotchas concernant des expressions régulières, je vous recommande le livre excellent de Jeffrey Friedl: Maîtriser des expressions régulières


1 commentaires

@kixx, merci pour l'explication et la recommandation du livre.