Accueil | Tutorial | Testeur (Fr) | Tester (En) | Man PCRE (En) | Les expressio

Accueil | Tutorial | Testeur (Fr) | Tester (En) | Man PCRE (En) | Les expressions régulières PCRE (Perl Compatible Regular Expressions) Remarques préliminaires Ce tutoriel est destiné aux développeurs qui maîtrisent déjà, un tant soit peu, la syntaxe de base des regex et des fonctions PHP qui les utilisent. Il mettra principalement l'accent sur différentes méthodes d'optimisation. Pour une présentation détaillée de la syntaxe élémentaire, il existe de nombreux tutoriaux sur le net notamment regular- expressions.info ou expreg.com. Les remarques qui vont suivre sur l'implémentation des PCRE par PHP ainsi que les conseils d'optimisation des motifs valent pour tous les langages qui utilisent les regex PCRE (Perl, bien sûr, mais aussi Python, Java, .NET etc...), avec des petites variations mineures. Tous ces langages partagent un même moteur, de type NFA (Nondeterministic Finite Automation). MySQL, awk et egrep, par contre, utilisent un autre type de moteur (DFA - Deterministic Finite Automation) plus simple, moins puissant mais plus rapide pour les cas (très) simples, voir simplistes ("Talking about DFA matching is very boring!" Jeffrey Friedl). Les bibliothèques d'expressions régulières de PHP Le support des expressions régulières dans PHP est assuré par deux bibliothèques: POSIX et PCRE. A première vue, les fonctions PCRE (preg_match...) ne diffèrent pas beaucoup des fonctions POSIX (ereg....). Le prototype des fonctions ainsi que la syntaxe des motifs se ressemblent fort. Mais c'est à première vue seulement! Les fonctions POSIX telles que ereg ou ereg_replace sont obsolètes à partir de la version PHP 5.3.0 et leur utilisation dans vos scripts produira une erreur. Il ne sera, dès lors, question dans ce tutoriel que d'optimisation des fonctions PCRE. Comment optimiser l'utilisation des expressions régulières. Page 1 sur 29 Tutoriel Expressions régulières PCRE 14/12/2014 http://lumadis.be/regex/tuto_pcre.php • Convention typographique • Quantificateur et gourmandise • Références arrières • Les parenthèses non capturantes • Remplacement avec "callback" • Les classes de caractères. • Les assertions simples • Choix des options: ◦ i - insensible à la casse ◦ s - le point (dot) reconnaît le retour ligne ◦ m - multilignes ◦ e - évaluation de code php ◦ U - ungreedy: quantificateurs non gourmands ◦ x - Pour commenter les motifs ◦ A, D, S, X - autres options moins courantes • Les assertions positive/négatives avant/arrières • Les masques conditionnels. • données binaires • Optimisation et astuces ◦ Utilisez le dot avec parcimonie ◦ Supprimez les parenthèses capturantes inutiles ◦ Optimisez les alternatives. ◦ Scindez vos regex trop complexes ◦ Supprimez les options inutiles ◦ Choisissez les fonctions callback appropriées ◦ Ancrez vos motifs ◦ Testez et chronométrez • man PCRE. • Quelques liens. Convention typographique Afin de distinguer les concordances (match) des captures, le code couleur suivant sera utilisé dans le texte: Le texte en concordance avec le motif sera surligné et les captures sont encadrées de lignes rouges . Le texte non concordant restera non surligné. La gourmandise est un vilain défaut Certes, mais pas dans le cas des expressions rationnelles, ce comportement est même recherché dans certains cas. Les quantificateurs +, * ou {1,} et {0,} sont gourmands par défaut dans les PCRE. Page 2 sur 29 Tutoriel Expressions régulières PCRE 14/12/2014 http://lumadis.be/regex/tuto_pcre.php Exemple: si on cherche à capturer l'url contenue dans l'ancre suivante <a href="http://ceci-est-une-url.com">lien</a> Dans ce motif preg_match('#<a href=(.*)>#', $txt, $out) la fonction capturera tout jusqu'au dernier > rencontré. Voyez plutôt: <a href= "http://ceci-est-une-url.com">lien</a > Par contre, dans les PCRE on peut demander au dot de s'arrêter au premier > rencontré grâce au point d'interrogation. Ainsi, la regex suivante preg_match('#<a href=(.*?)>#', $txt, $out) , retournera ceci: <a href= "http://ceci-est-une-url.com" >lien</a> Démo Oui, oui, je sais qu'ici on aurait pu utiliser utilement une classe négative de caractères telle que '[^>]*' pour obtenir le même le résultat plus rapidement encore (voir discussion plus bas), mais c'est juste pour l'exemple! retour menu principal Les références arrières (back reference) Dans un motif PCRE, on peut insérer une référence arrière. C'est à dire qu'on peut capturer quelque-chose dans un texte et l'utiliser plus loin dans le motif comme référence. Une utilisation appropriée des références arrières permet de construire des motifs intelligents. Exemple, dans la chaîne : <i>Texte italique</i> texte normal <b> et texte gras</b> fin du texte. Imaginons que nous souhaitions capturer tout ce qui est contenu dans les balises <b>ou <i>, La regex suivante est une des solutions possibles preg_match_all('#< ([ib])>(.*?)</\1>#', $txt, $out) La première parenthèse capturante mémorisera le i ou le b des balises ouvrantes <b>ou <i> et l'utilisera plus loin dans les balises fermantes </b>ou </i> grâce au motif </\1> où \1, la référence arrière, sera égal à b ou i selon ce qui aura été capturé plus avant. Résultat: Page 3 sur 29 Tutoriel Expressions régulières PCRE 14/12/2014 http://lumadis.be/regex/tuto_pcre.php < i > Texte italique </i> texte normal < b > et texte gras </b> fin du texte. Et le tableau de sortie $out contiendra les éléments suivants: • Les matches $out[0]=> ◦ [0]=> <i>Texte italique</i> ◦ [1]=> <b> et texte gras</b> • Capture $out[1]=> ◦ [0]=> i ◦ [1]=> b • Capture $out[2]=> ◦ [0]=> Texte italique ◦ [1]=> et texte gras Démo Chaque parenthèse capturante rencontrée stockera la capture dans une référence arrière numérotée en fonction de la position de la parenthèse dans le motif. Ainsi avec le motif #((A)BCD)# on retrouvera ABCD dans la référence arrière \1 et A dans \2 et ainsi de suite. Il peut y avoir jusqu'à 99 références arrières. Le jour où vous vous retrouvez avec 99 groupes de parenthèses capturantes, vous avez du souci à vous faire pour l'optimisation de votre motif! Dans certains cas, il peut s'avérer intéressant de ne pas incrémenter les références arrières pour en faciliter le traitement. Dans l'exemple qui suit, vous souhaitez capturer tous les jours de la semaine dans ce texte: Le premier jour de la semaine est le lundi suivi par mardi, le mercredi puis le jeudi qui précède le vendredi. Dans le but d'éviter une énumération de tous les jours de la semaine dans votre motif, vous pourriez écrire: #((lun|mar|jeu)|(mer)cre|(ven)dre) di\b# ce motif aura 4 groupes de captures puisqu'il y a 4 groupes de parenthèses avec un niveau d'imbrication. Voyez: • Capture [1]=> array ◦ [0]=>lun ◦ [1]=>mar ◦ [2]=>mercre ◦ [3]=>jeu ◦ [4]=>vendre • Capture [2]=> ◦ [0]=>lun ◦ [1]=>mar ◦ [2]=> ◦ [3]=>jeu Page 4 sur 29 Tutoriel Expressions régulières PCRE 14/12/2014 http://lumadis.be/regex/tuto_pcre.php ◦ [4]=> • Capture [3]=> ◦ [0]=> ◦ [1]=> ◦ [2]=>mer ◦ [3]=> ◦ [4]=> • Capture [4]=> ◦ [0]=> ◦ [1]=> ◦ [2]=> ◦ [3]=> ◦ [4]=>ven Démo La ve rsion Pe rl 5.10 a introduit la possiblilité de ne pas incréme nte r la numérotation de s capture s dans une série d'alte rnative s (Duplicate Subpatte rn Numbe rs). Il e st dès lors possible de donne r le même numéro de capture aux pare nthèse capturante s. A ce tte fin, le motif ci de ssus s'écrira: # (?|(lun|mar|jeu)|(mer)cre|(ven)dre)di\b#. Re marque z le ?| de la pre mière pare nthèse . Ce motif produira un se ul groupe de capture : • Capture [1]=> ◦ [0]=>lun ◦ [1]=>mar ◦ [2]=>mer ◦ [3]=>jeu ◦ [4]=>ven Il e st mainte nant très facile d'utilise r ce tte unique référe nce arrière pour faire un re mplace me nt par e xe mple . Voye z dans le te ste ur ce que donne par e xe mple : preg_replace('#(?|(lun|mar|jeu)|(mer)cre|(ven)dre) di\b#', '\1', $texte) re tour me nu principal Les parenthèses non capturantes Le s pare nthèse s sont principale me nt utilisée s pour capture r une série de caractère s corre spondant à un motif. Dans ce rtains cas, e lle s se ront utilisée s pour délimite r le s alte rnative s ou bie n une chaîne alte rnative . Ainsi dans le te xte suivant: Page 5 sur 29 Tutoriel Expressions régulières PCRE 14/12/2014 http://lumadis.be/regex/tuto_pcre.php Astuce : PHP supporte aussi des expressions rationnelles compatibles Perl, avec l'extension PCRE functions. Ces fonctions supportent des recherches non-gourmandes, des assertions, des sous-masques conditionnels et toute une gamme de fonctionnalités absentes des expressions rationnelles POSIX. Admettons qu'on cherche à capturer tous les mots précédés de 'des ou 'de', on pourrait faire preg_match_all('#\b(le|les|la|de|des|du)\s(\w+) \b#', $txt, $out) . Ce qui donne: Astuce : PHP supporte aussi des expressions rationnelles compatibles Perl, avec l'extension PCRE functions. Ces fonctions supportent des recherches non-gourmandes, des assertions , des sous -masques conditionnels et toute une gamme de fonctionnalités absentes des expressions rationnelles POSIX. Et le tableau de résultat $out retournera • Matches [0]=> ◦ [0]=> des expressions ◦ [1]=> des recherches ◦ [2]=> des assertions ◦ [3]=> des sous ◦ [4]=> de fonctionnalités ◦ [5]=> des expressions • Captures [1]=> ◦ [0]=> des ◦ [1]=> des ◦ [2]=> des ◦ [3]=> des ◦ [4]=> de ◦ [5]=> des • Captures [2]=> ◦ [0]=> expressions ◦ [1]=> recherches ◦ [2]=> assertions ◦ [3]=> sous ◦ [4]=> fonctionnalités ◦ [5]=> expressions On voit que les parenthèses du groupe de l'alternative ont effectivement capturé tous les déterminants (de, des etc...). Mais seul le deuxième groupe Page uploads/s3/ lumadis-be-regex-tuto-pcre-pdf.pdf

  • 32
  • 0
  • 0
Afficher les détails des licences
Licence et utilisation
Gratuit pour un usage personnel Attribution requise
Partager