Expressions rgulires en PHP Hugo Etivant Dernire mise jour : 15 avril 2004 2 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Prsentation Tout programmeur sest dj vu oblig de traiter des chanes de caractres. Sur le web, les pages elles-mmes, les donnes transmises aux scripts et celles provenant des bases de donnes sont des chanes de caractres quil faut traiter, analyser, corriger Chose ardue et quasi-impossible sans lutilisation dun formidable outil que sont les expressions rgulires (dites aussi expressions rationnelles). Nous nexpliquerons pas lalgorithmique sous jacente trs complexe (machines tats, automates, graphes, rcursivit) mais illustrerons seulement son utilisation via les fonctions de PHP. On utilisera par la suite le terme regex terme emprunt langlais pour dsigner une expression rgulire. A noter que les fonctions PHP dont il est question ici sont conformes la norme POSIX et hrit du langage Perl. 3 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Motifs Une regex sapparente une expression mathmatique, car on y trouve des oprateurs, des valeurs et des variables. Les regex permettent de se lancer la recherche de motifs dcrits par la combinaison doprateurs et de valeurs. Les fonctions de recherche de motifs du PHP retournent vrai si le motif a t trouv dans une chane de caractres, elles permettent aussi dextraire de cette chane la sous chane qui correspond au motif et de la modifier. Une utilisation rcurrente des regex consiste en la recherche de mots cls dans des fichiers ou dans une base de donnes ou encore en la vrification des donnes saisies par lutilisateur afin de sassurer quelles respectent un format prdfini, ou mme doprer des conversions de format. 4 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Exemple Par exemple on peut se lancer la recherche du mot voiture dans la chane $str : if(ereg(voiture, $str)) { echo ok; } else { echo invalide; } Le motif ici est rduit sa plus simple expression : voiture est le motif de recherche, il consiste juste en une valeur (chane de caractres). On peut le compliquer pour accepter une majuscule en dbut de mot : [Vv]oiture. On pourra galement interdire que ce motif soit inclus dans un mot plus grand comme voiturette : [Vv]oiture([^[:alpha:]]|$). Mais autoriser son pluriel : [Vv]oiture(s)?([^[:alpha:]]|$). Vous voyez, a devient vite du charabia ! 5 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Les fonctions PHP ereg($motif, $chane [, $vars]) : retourne VRAI si le motif $motif est trouv dans la chane $chane. Le tableau $vars contiendra les sous chanes de $chane vrifiant le motif. ereg_replace($motif, $nouvelle, $chane) : retourne la chane $chane dont les sous chanes vrifiant le motif $motif sont remplaces par la chane $nouvelle. split($motif, $chane [, $num]) : retourne un tableau dau maximum $num lments des sous chanes de $chane qui se trouvent spares par des dlimiteurs vrifiant le motif $motif. Les fonctions eregi(), eregi_replace(), spliti() sont identiques aux prcdentes mais insensibles la casse. Note : la casse est la diffrence majuscules/minuscules. 6 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Description des motifs Les motifs sont dcrits par ces trois caractristiques : les caractres, chanes ou classes de caractres qui les composent leur nombre dapparition leur position les alternatives 7 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Chane de caractres Un motif peut tre constitu dune simple chane. Exemple : ereg(Paris, Je vis Paris.) Cet exemple renvoie VRAI car le motif Paris a t trouv dans la chane Je vis Paris.. Exemple : ereg(hugo, Hugo Pratt fut un grand dessinateur de BD.) Cet exemple renvoie FAUX car le motif hugo na pas t trouv dans la chane Hugo Pratt fut un grand dessinateur de BD.. Attention la casse des caractres ! 8 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Alternative Un peut dcider dimposer la prsence dune chane parmi plusieurs grce au caractre spcial de signification OU boolen : | . Exemple : ereg(hugo|Hugo|HUGO, Hugo Pratt fut un grand dessinateur de BD.) Cet exemple renvoie VRAI car le motif a t trouv dans la chane Hugo Pratt fut un grand dessinateur de BD.. Et cest en particulier Hugo qui a t trouv. Exemple : $motif = hugo|Hugo|HUGO; $str = Hugo Pratt fut un grand dessinateur de BD.; if(ereg($motif, $str, $regs)) foreach($regs as $elem) echo $elem.<br />; Cet exemple recherche et affiche le motif trouv. Ici ce sera Hugo. 9 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Ensemble de caractres (I) On peut vouloir recherche une chane complte : voiture ou bien seulement un caractre parmi un ensemble. Les ensembles sont dfinis entre crochets [ ]. Pour rechercher lune des voyelles dans un mot, on utilisera le motif suivant : [aeiouy]. Exemple : ereg([aeiouy], voiture) Cet exemple renvoie VRAI puisque le mot voiture contient au moins une des voyelles dfinies dans le motif. Pour rechercher une plage de caractres, on indiquera le premier et le dernier caractres spars par un tiret pour demander de rechercher un caractre parmi ceux de lalphabet situs entre ces deux caractres. Pour rechercher les caractres entre a et d dans le mot voiture : Exemple : ereg([a-d], voiture) Cet exemple renvoie FAUX car le mot voiture ne contient aucune des lettres de lalphabet comprises entre a et b. 10 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Ensemble de caractres (II) On peut tendre notre logique aux chiffres. Pour rechercher un chiffre entre 0 et 9, le motif sera le suivant : [0-9]. Exemple : ereg([0-9], voiture) Cet exemple renvoie FAUX car la chane voiture ne contient aucun des chiffres parmi ceux de lensemble du motif. On peut ajouter notre ensemble loprateur de ngation ^. Cet oprateur ne peut apparatre quen dbut densemble et sapplique tout lensemble. Exemple : ereg([^0-9], voiture) Cet exemple renvoie VRAI car effectivement, la chane voiture ne contient aucun des chiffres parmi ceux de lensemble du motif. Autre exemple : ereg([^aeiouy], voiture) Cet exemple renvoie FAUX puisque le mot voiture contient au moins une des voyelles dfinies dans le motif. 11 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Ensemble de caractres (III) Il est possible de combiner ensembles et plages de caractres. Exemple : ereg([a-zA-Z], voiture) Cet exemple renvoie VRAI car la chane voiture contient au moins un des caractres dfinis par le motif. Le motif dfini tous les caractres minuscules entre a et z ainsi que tous les caractres majuscules entre A et Z. Exemple : ereg([^a-zA-Z], voiture) Ici on se demande si notre chane vrifie le motif suivant : ne pas trouver de lettres quelles soient minuscules ou majuscules. Cet exemple renvoie FAUX puisque voiture contient des lments du motif : des caractres minuscules. 12 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Classes de caractres (I) Il existe des ensembles prdfinis de caractres, chacun portant un nom particulier. Ainsi, lensemble des chiffres : [0-9] sappelle [[:digit:]]. Les exemples suivants sont quivalents : ereg(0|1|2|3|4|5|6|7|8|9, $chaine) ereg([0-9], $chaine) ereg([[:digit:]], $chaine) Les exemples suivants sont quivalents : ereg([^[:alnum:]], $chaine) ereg([^[:alpha:][:digit:]], $chaine) ereg([^A-Za-z0-9], $chaine) Les exemples suivants sont quivalents : ereg([[:alpha:]], $chaine) ereg([[:upper:][:lower:]], $chaine) ereg([A-Za-z], $chaine) 13 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Classes de caractres (II) Caractres imprimables, excepts ceux de contrle [ -~] [[:print:]] Caractres dchappement [\x00-\x19\x7F] [[:cntrl:]] Tout type despace [ \t\v\f] [[:space:]] Caractres de ponctuation [!-/:-@[-{-~] [[:punct:]] Caractres en majuscule [A-Z] [[:upper:]] Caractres en minuscule [a-z] [[:lower:]] Caractres affichables et imprimables [!-~] [[:graph:]] Caractres hexadcimaux [0-9a-fA-F] [[:xdigit:]] Espaces ou tabulations [\x09] [[:blank:]] Caractres numriques [0-9] [[:digit:]] Caractres alphabtiques [A-Za-z] [[:alpha:]] Caractres alphanumriques [A-Za-z0-9] [[:alnum:]] Description Equivalent Squence 14 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Caractres spciaux Les caractres spciaux sont ceux qui possdent une signification particulire aux yeux des rgles de construction des motifs des regex. Ces caractres ne peuvent pas tre utiliss comme nimporte quel autre, sauf les prcder dun antislash \. Ils sont les suivants : ^ . [ ] $ ( ) | * + ? { } \ Toutefois ces caractres (sauf ] et -) perdent leur caractre spcial lorsquils sont utiliss entre crochets. Comme en C, pour despcialiser un caractre, il faut le faire prcder dun antislash \. A noter quen dehors des crochets, le tiret - na pas signification particulire. Pour utiliser malgr tout les caractres ] et - entre crochets, il faudra ruser, et les placer respectivement en dbut et en fin densemble. Exemple : ereg([][(){}], $chaine) tudions le comportement de PHP face ce motif : il rencontre un premier crochet ouvrant quil considre comme spcial et prcdant la dfinition dun ensemble de caractres. Puis vient le crochet fermant, comme lensemble vide nest pas connu par les regex PHP, il considre ce crochet comme nimporte lequel des caractres normaux. Ensuite vient le crochet ouvrant, comme le mode ensemble est dj actif, il ne vas en ouvrir un autre et considre ce crochet comme un caractre normal. Et vient enfin le dernier caractre le crochet fermant qui clt le mode ensemble. 15 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Cardinalit Un caractre ou un ensemble de caractres peut tre interdit, facultatif, obligatoire ou rpt un certain nombre de fois selon la syntaxe qui laccompagne. Obligatoire : apparat une ou plusieurs fois + Doit apparatre exactement n fois {n} Doit apparatre au moins n fois {n,} Doit apparatre entre n et m fois avec n<m {n,m} Facultatif : apparat zro, une ou plusieurs fois * Facultatif : apparat une ou zro fois ? description syntaxe Exemple : ereg([[:lower:]]?[[:digit:]]{4}, la voiture K2000 est intelligente) Retourne VRAI car la sous chane K2000 contient un caractre minuscule optionnel ([[:lower:]]?) suivi de quatre chiffres obligatoires ([[:digit:]]{4}). 16 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Caractre Pour chercher un caractre nimporte lequel (y compris les caractres de fonction) : . (point). Exemple : ereg(http://.+\.com, http://cyberzoide.developpez.com) Cet exemple retourne VRAI car http://cyberzoide.developpez.com commence par http:// suivi de nimporte quel caractre . prsent une ou plusieurs fois et finissant par .com (dont le point est despcialis). 17 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Position On peut insrer dans le motif des contraintes de positions dans la chane : dbut et fin. Fin de chane $ Dbut de chane ^ description syntaxe Exemple : ereg(^[[:upper:]].+\.$, Les Misrables.) Retourne VRAI car Les Misrables. commence par une majuscule et fini par un point (avec entre les deux un nombre indfini de caractres). Exemple : ereg(^\$, $5.000) Retourne VRAI car $5.000 commence par le symbole de lunit montaire amricaine, le dollars. Le caractre de position de fin de chane a t despcialis par un antislash. 18 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Exercice format montaire Ex. 1 : Construire un motif permettant de vrifier la validit dune chane comportant un prix en Euros. Le prix pourra comporter de 0 2 dcimales. La virgule sera le sparateur de dcimales. Les milliers (groupes de 3 chiffres) seront spars par un point. Le prix se terminera par EUR . La chane ne devra rien comporter dautre. Solution : ereg(^[0-9]{1,3}(\.[0-9]{3})*(,[0-9]{0,2})? EUR$, $str) ^xxx EUR$ : la chane contient seulement le nombre xxx suivi dun espace et de lunit EUR . (,[0-9]{0,2})? : le nombre contient optionnellement des dcimales introduites par une virgule, le nombre de dcimales varie de 0 2 (\.[0-9]{3})* : il y a 0 ou plusieurs groupes de milliers spars par un point [0-9]{1,3} : il y a 1 ou 3 chiffres au minimum dans notre nombre 5.000,00 EUR 100,555 EUR 5.299.138,25 EUR 30.5 EUR 1.500 EUR 1 500 EUR 20,5 EUR 25, EUR 0 EUR mauvais bon 19 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Remplacement Les expressions ne se limitent pas la recherche de motifs mais permettent aussi de remplacer les sous-chanes satisfaisant un motif par une autre chane via la fonction ereg_replace(). Exemple 1 : $str = Je roule en voiture.; $str = ereg_replace(voiture, automobile, $str); echo $str ; // affiche : Je roule en automobile. Exemple 2 : $str = cyberzoide@yahoo.fr; $str = ereg_replace(@(.+)\.fr, @wanadoo.fr, $str); echo $str ; // affiche : cyberzoide@wanadoo.fr 20 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Fractionnement Il est possible de fractionner une chane en plusieurs sous-chanes spares par un dlimiteurs satisfaisant un motif, en utilisant la fonction split(). Exemple 1 : $str = Hugo:Etivant:cyberzoide@yahoo.fr; $tab = split(:, $str, 3); foreach($tab as $elem) { echo $elem, <br />; } Cet exemple spare les 3 premires sous-chanes dlimites par le caractre deux points comme cela se fait pour lanalyse dune ligne du fichier .passwd. Exemple 2 : $str = 23-03-2003; list($jour, $moi, $an) = split([./-], $str); Cet exemple spare les sous-chanes dune date dont le sparateur peut tre lun de la plage suivante : point, slash et tiret. // affiche : Hugo Etivant cyberzoide@yahoo.fr 21 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Parenthses capturantes (I) Syntaxe : ereg($motif, $chaine, $regs); Les sous-chanes de $chaine correspondantes au $motif de recherche sont enregistres dans le tableau $regs si ce dernier argument optionnel de ereg() est spcifi. $regs[0] contient une copie de la chane dorigine $chaine. $regs[1] contiendra la premire parenthse capturante (celle qui commence le plus tt), $regs[2] contiendra la deuxime parenthse capturante (celle qui commence aprs la premire), et ainsi de suite. Exemple : $email = "cyberzoide@yahoo.fr"; ereg("^(.+)@(.+)\.(.+)$", $email, $regs); echo $regs[1], ':', $regs[2], ':', $regs[3]; // affiche cyberzoide:yahoo:fr Cet exemple extrait dune adresse email le compte, le domaine et le suffixe gographique/catgoriel. 22 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Parenthses capturantes (II) Un autre atout des regex est de pouvoir capturer la sous-chane satisfaisant le motif afin de linclure dans une chane de remplacement. Pour cela, on va employer les parenthses afin dencadrer la sous-chane du motif quil conviendra de capturer (que lon appellera instance ). Puis, on fera rfrence cette instance du motif via la syntaxe suivante : \\X o X est le numro de la parenthse que lon souhaite capturer. Car il est possible de capturer 9 instances. Linstance \\0 faisant rfrence la chane en entier et \\1 la premire parenthse capturante. Exemple : $date = "21-02-2003"; $date = ereg_replace("([[:digit:]]{2})-([[:digit:]]{2})-([[:digit:]]{4})", "\\2/\\1/\\3", $date); // affiche 02/21/2003 Cet exemple convertit une date MySQL au format francophone. Le mme rsultat aurait pu tre obtenu par lutilisation successive des fonctions : split(), list() et ereg_replace(). Mais grce aux parenthses capturantes, on a tout fait en une seule commande ! 23 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Autres fonctions de traitement de chanes Les expressions rgulires sont un outil puissant pour traiter des chanes de caractres dont on connat le schma, cest--dire la manire gnrale dont elles sont grammaticalement composes. Malgr leurs qualits, elles souffrent dun dfaut majeur : la lenteur dexcution du moteur de traitement de ces chanes. Ainsi, il est recommand dutiliser le plus souvent possible les fonctions standard de traitement des chanes de caractres afin dacclrer le temps de traitement de vos scripts. On peut organiser ces fonctions en 3 classes : - recherche, comparaison (similar_text, strcmp, strnatcmp, strcasecmp, strncmp, substr, strstr, strspn, strpos) - remplacement (AddSlashes, AddCSlashes, htmlentities, htmlspecialchars, QuoteMeta, trim, nl2br, strip_tags, StripSlashes, str_pad, str_repeat, str_replace, strtr, ucfirst, ucword, wordwrap) - fractionnement (explode, implode, join, chunk_split, strtok) 24 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Tableau comparatif Certaines fonctions standard sont quivalentes une expression rgulire, il faudra alors privilgier ces premires : ereg_replace(a, @, $str) strtr($str, a, @) split($op, $str) strtok($str, $op) ereg_replace(\[^\], , $str) stripslashes($str) ereg_replace(<.+>, , $str) strip_tags($str) ereg(^$str1$, $str2); strcmp($str1, $str2) ereg_replace(^( )+,, $str) ltrim($str) ereg_replace(\n, <br />,$str) nl2br($str) Fonction regex Fonction standard 25 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Les fonctions standard (I) strcmp($str1, $str2) : compare en binaire les 2 chanes, retourne un entier ngatif si $str1<$str2, positif si $str1>$str2, nul si $str1=$str2. strncmp($str1, $str2, $i) : comme strcmp() mais sur les $i premiers caractres. strcasecmp($str1, $str2) : comme strcmp() mais insensible la case. strncasecmp($str1, $str2) : comme strncmp() mais insensible la case. strnatcmp($str1, $str2) : comme strcmp() mais dans lordre naturel (0- 9,a-z,A-Z). strnatcasecmp($str1, $str2) : comme strcasecmp() mais dans lordre naturel . strstr($str1, $str2) : retourne le contenu de $str1 depuis la premire occurrence de $str2 jusqu la fin. stristr($str1, $str2) : comme strstr() mais insensible la casse. strrchr($str1, $str2) : comme strstr() mais partir de la dernire occurrence. substr($str, $i [, $n]) : retourne la sous-chane de $str dbutant la position $i jusqu $n. 26 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Les fonctions standard (II) addslashes($str) : retourne la chane $str dont les caractres , et \ sont protgs par un antislash. stripslashes($str) : fonction rciproque de addslashes. quotemeta($str) : ajoute un antislash devant les caractres suivants : . \\ + * ? [ ^ ] ( $ ). htmlspecialchars($str) : convertit tous les caractres spciaux en leur code HTML, par exemple < devient < Synonyme : htmlentities(). ltrim($str) : supprime les espaces de dbut de chane rtrim($str) : supprime les espaces de fin de chane, synonyme : chop() trim($str) : ltrim() + rtrim() explode($op, $str [, $n]) : scinde la chane $str en au plus $n morceaux en utilisant le sparateur $op. Retourne un tableau. implode($op, $tab) : fonction rciproque de explode(). strtok([$str,] $op) : morcelle la chane $str via le sparateur $op. chunk_split($str1 [, $n [, $str2]]) : morcelle $str1 par insertion de $str2 tous les $n caractres. 27 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Les fonctions standard (III) ord($str) : retourne la valeur ASCII du caractre chr($str) : fonction rciproque de ord() strlen($str) : retourne la taille de $str (i.e. le nombre de caractres) str_pad($str1, $n [, $str2 [, $type]]) : complte la chane $str1 avec $n fois $str2 (ou $n espaces par dfaut) en fin de chane par dfaut ou en dbut ($type = STR_PAD_LEFT) ou encore sur les deux cts (STR_PAD_BOTH). str_repeat($str, $n) : rpte $n fois la chane $str. strrev($str) : inverse une chane strpos($str1, $str2 [, $n]) : recherche la premire occurrence de $str2 dans la chane $str1 partir de la position $n (au dbut par dfaut). strrpos($str1, $str2) : comme strpos() mais un caractre partir de la fin substr_count($str1, $str2) : compte le nombre doccurrences de $str2 dans $str1 28 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Les fonctions standard (IV) substr_replace($str1, $str2, $i [, $n]) : remplace $n caractres depuis $i dans $str1 par $str2 strtolower($str) : conversion en minuscules strtoupper($str) : conversion en majuscules ucfirst($str) : conversion en majuscule du premier caractre de la chane ucwords($str) : conversion en majuscule du premier caractre de chaque mot wordwrap($str [, $n [, $op [, $test]]]) : ajoute la csure $op dans $str tous les $n caractres. Si $test vaut 1, les mots trop long seront coups. Par dfaut dcoupe tous les 75 caractres par \n. nl2br($str) : remplace les sauts de ligne \n par la balise XHTML <br />. 29 http://cyberzoide.developpez.com Le CyberZode Qui Frtille Historique 15 avril 2004 : corrections mineures 3 mars 2003 : quelques ajouts, plus dexemples (29 diapos) 23 fvrier 2003 : parenthses capturantes, fonctions standard (28 diapos) 23 dcembre 2002 : cration du document (19 diapos) Agissez sur la qualit de ce document en envoyant vos critiques et suggestions lauteur. Pour toute question technique, se reporter au forum PHP de Developpez.com Hugo Etivant cyberzoide@yahoo.fr http://cyberzoide.developpez.com/