Dans le monde des expressions régulières, il existe des modificateurs qu'on surnomme "lazy" (paresseux) et "greedy" (gourmand). C'est important de savoir comment ça fonctionne car non seulement ça permet d'optimiser la performance de la correspondance, mais ça évite surtout des surprises.
Note : si vous voulez suivre ce petit tutoriel pas à pas, je vous suggère d'ouvrir une nouvelle fenêtre de regular expression tool et de sélectionner l'onglet PHP PCRE ou JavaScript, en mode "match".
Imaginez la chaîne de caractères ci-dessous :
Je mange des <strong>chips</strong> et je bois une bière
Et la regex suivante : <.+>
Effectivement, ça trouve <strong>chips</strong>.
Pourquoi est-ce que ça ne conserve pas que la première balise strong ? Parce que les opérateurs comme +, *, {} sont greedy, ce qui veut dire qu'ils ont tendance à faire correspondre la plus grande partie de la chaîne de caractères. Comme on spécifie le point et le plus, il ne s'arrête pas au premier >. Il poursuivra son chemin jusqu'à la fin (car > fait parti des caractères acceptés par le point) et il fera marche arrière pour trouver le dernier > qui équivaut à la fin du pattern spécifié.
Comment peut-on faire pour arrêter la correspondance de façon à ce qu'il en prenne le moins possible, c'est-à-dire sans backtrack ? La réponse : en utilisant un modificateur "lazy" comme le symbole "?".
En modifiant la regex initiale pour celle-ci : <.+?>, l'engin s'arrêtera aussitôt qu'il fera correspondre une section de la chaîne de caractères. Autrement dit, il en fera correspondre le moins possible.
Le résultat : seule la balise <strong> sera retenue.
jeudi 30 juillet 2009
3 réponses à "Regex greedy vs. lazy"
S'abonner à :
Publier des commentaires (Atom)
Hello,
Voilà quelque chose que j'ignorais complètement sur les regex. Est-ce que c'est équivalent à l'utilisation de l'option U (ungreedy) ou cela améliore les performances ?
Merci en tout cas.
Oui, c'est équivalent à utiliser /U avec PHP PCRE mais comme le flag n'est pas disponible dans toutes les implémentations de regex, on pourra opter pour ? de façon plus générale. Pour la performance, je suppose que c'est équivalent.
preg_match('/<.+>/iU', 'string', $result);
Ok, merci !