Partie
difficile, d'où les 2 parties, mais qui permet de faire des tas de trucs.
A quoi ça sert ? C'est un système très puissant et très rapide pour faire des recherches dans des chaînes de caractères (des phrases par exemple). C'est une sorte de fonctionnalité Rechercher / Remplacer très poussée, dont on ne pourra plus se passer une fois qu'on saura vous en servir.
Des exemples ?
- Vérifier automatiquement si l'adresse e-mail entrée par le visiteur a une forme valide (comme "dupont@free.fr")
- Modifier une date qu'on a au format américain (08-05-1985) pour la mettre dans le bon ordre en français (05/08/1985)
- Remplacer automatiquement toutes les adresses "http://" par des liens cliquables, comme ça se fait sur certains forums.
- Ou encore créer son propre langage simplifié à partir du HTML, comme le fameux bbCode ([b][/b]…)
Sommaire
Où utiliser une Regex ?
POSIX ou PCRE ?
Il existe 2 types d'expressions régulières :
- POSIX : c'est un langage d'expressions régulières mis en avant par PHP, qui se veut un peu plus simple que PCRE (ça n'en reste pas moins assez complexe). Toutefois, son principal et gros défaut, c'est que ce "langage" est plus lent que PCRE.
- PCRE : ces expressions régulières sont issues d'un autre langage (le Perl). Considérées comme (un peu) plus complexes, elles sont surtout bien plus rapides et performantes.
PHP propose de choisir entre POSIX et PCRE. Pour les raisons citées au-dessus, on va voir PCRE.
Les fonctions qui nous intéressent
Il existe plusieurs fonctions utilisant le "langage PCRE" qui commencent toutes par "preg_" :
- preg_grep
- preg_split
- preg_quote
- preg_match
- preg_match_all
- preg_replace
- preg_replace_callback
 |
Chaque fonction a sa particularité, certaines permettent de faire simplement une recherche, d'autre une recherche / remplacement, mais leur gros point commun c'est qu'elles utilisent un "langage" identique pour faire une recherche.
Lorsqu'on aura appris le langage PCRE, on pourra utiliser chacune d'elles sans problème. |
Pour éviter trop de théorie, on va commencer pour s'entraîner à utiliser une de ces fonctions :
preg_match.
preg_match
En utilisant cette fonction, on pourra s'exercer et voir petit à petit si on comprend le principe du langage PCRE.
Il faut juste savoir que cette fonction renvoie un booléen : VRAI ou FAUX (true ou false en anglais). Elle renvoie true (vrai) si elle a trouvé le mot qu'on cherche dans la chaîne, faux (false) si elle ne l'a pas trouvé.
On doit lui donner 2 informations : notre regex (c'est le petit surnom qu'on donne à "expression régulière") et la chaîne dans laquelle on fait une recherche.
Voici par exemple comment on peut s'en servir, à l'aide d'une condition if :
Code PHP
<?php
if (preg_match("** Notre REGEX **", "Ce dans quoi on fait la recherche"))
{
echo 'le mot qu'on cherche se trouve dans la chaîne';
}
else
{
echo 'le mot qu'on cherche ne se trouve pas dans la chaîne';
}
?>
A la place de "** Notre REGEX **", on tapera quelque chose en langage PCRE, comme :
#(((https?|ftp)://(w{3}\.)?)(?<!www)(\w+-?)*\.([a-z]{2,4}))#
C'est à ce langage incompréhensible qu'on va s'intéresser par la suite.
Des recherches simples
On va commencer à faire des recherches très simples et très basiques. C'est en mélangeant tout après que ça se compliquera.
Première chose importante à savoir : une regex (= expression régulière) est toujours entourée de caractères spéciaux appelés
délimiteurs.
On peut choisir n'importe quel caractère spécial comme délimiteur, et pour éviter de tourner en rond trop longtemps on va en imposer un : le dièse !
Notre regex se trouve alors entourée de dièses, comme ceci :
#Ma regex#
 |
Mais à quoi servent les dièses, puisque de toute façon la regex est entourée par des guillemets dans la fonction PHP ? |
Parce que, si on veut, on peut utiliser des options. On ne va pas parler des options tout de suite (on n'en a pas besoin pour commencer), mais il faut savoir que ces options se mettent après le second dièse, comme ceci :
#Ma regex#Options
A la place de "Ma regex", on doit mettre le mot qu'on recherche.
Prenons un exemple : on aimerait savoir si une variable contient le mot "guitare". Il nous suffit d'utiliser la regex suivante pour faire la recherche :
#guitare#
Dans un code PHP, ça donne :
Code PHP
<?php
if (preg_match("#guitare#", "J'aime jouer de la guitare."))
{
echo 'VRAI';
}
else
{
echo 'FAUX';
}
?>
Si on essaie ce script, il affiche VRAI parce que le mot guitare a été trouvé dans la phrase "J'aime jouer de la guitare."
Bien retenir ce petit bout de code, on va le garder un moment en changeant parfois la regex, parfois la phrase dans laquelle on fait la recherche.
Pour bien comprendre comment les regex se comportent, présentons les résultats dans un tableau :
Chaîne | Regex | Résultat |
J'aime jouer de la guitare. | #guitare# | VRAI |
J'aime jouer de la guitare. | #piano# | FAUX |
On a trouvé le mot "guitare" dans la première regex, mais pas "piano" dans la seconde.
Et tu casses, tu casses, tu casses…
Important à savoir : normalement, les regex font la différence entre majuscules et minuscules (elles sont "sensibles à la casse"). Par exemple :
Chaîne | Regex | Résultat |
J'aime jouer de la guitare. | #Guitare# | FAUX |
J'aime jouer de la guitare. | #GUITARE# | FAUX |
Comment faire si on veut que nos regex ne fassent plus la différence entre majuscules et minuscules ?
On va utiliser justement une option. C'est la seule qu'on aura besoin de retenir pour le moment. Il faut rajouter la lettre "i" après le 2ème dièse, et la regex ne fera plus attention à la casse :
Chaîne | Regex | Résultat |
J'aime jouer de la guitare. | #Guitare#i | VRAI |
Vive la GUITARE ! | #guitare#i | VRAI |
Vive la GUITARE ! | #guitare# | FAUX |
Le symbole OU
On va maintenant utiliser le symbole OU, qu'on a déjà vu dans le chapitre sur les conditions : c'est la barre verticale "
|".
Grâce à cela, on va pouvoir laisser plusieurs possibilités à notre regex. Ainsi, si on tape :
#guitare|piano#
,
cela veut dire qu'on cherche soit le mot "guitare"
ou le mot "piano". Si un des 2 mots est trouvé, la regex répond VRAI.
Quelques exemples :
Chaîne | Regex | Résultat |
J'aime jouer de la guitare. | #guitare|piano# | VRAI |
J'aime jouer du piano. | #guitare|piano# | VRAI |
J'aime jouer du banjo. | #guitare|piano# | FAUX |
J'aime jouer du banjo. | #guitare|piano|banjo# | VRAI |
Début et fin de chaîne
Les regex permettent d'être très précis. Jusqu'ici en effet, le mot pouvait se trouver n'importe où. Mais supposons que l'on veuille que la phrase commence ou se termine par ce mot ?
On va avoir besoin des deux symboles suivants :
- ˆ (accent circonflexe) : indique le début d'une chaîne.
- $ (dollar) : indique la fin d'une chaîne.
Ainsi, si on veut qu'une chaîne commence par "Bonjour", il faudra utiliser la regex :
#ˆBonjour#
Si on met le symbole "ˆ" devant le mot, alors ce mot devra obligatoirement se trouver au début de la chaîne, sinon on nous répondra FAUX.
De même, si on veut vérifier que la chaîne se termine par "gars", on écrira cette regex :
#gars$#
Une série de tests :
Chaîne | Regex | Résultat |
Bonjour petit gars | #ˆBonjour# | VRAI |
Bonjour petit gars | #gars$# | VRAI |
Bonjour petit gars | #ˆgars# | FAUX |
Bonjour petit gars !!! | #gars$# | FAUX |
Les classes de caractères
Grâce à ce qu'on appelle les classes de caractères, on peut faire varier énormément les possibilités de recherche.
Dans une regex, on place une classe de caractères entre crochets. Cela permet de mettre énormément de possibilités de recherche à la fois, tout en restant très précis.
Des classes simples
Regardons cette regex :
#gr[io]s#
Entre crochets, c'est ce qu'on appelle la classe de caractères. Cela signifie qu'une des lettres à l'intérieur peut convenir.
Dans ce cas-ci, notre regex reconnaît 2 mots : "gris" et "gros". C'est un peu comme le OU qu'on a appris tout à l'heure, sauf que ça s'applique ici à une lettre et non pas à un mot.
D'ailleurs, si on met plusieurs lettres comme ceci :
#gr[ioa]s#
Cela signifie "i" OU "o" OU "a". Donc notre regex reconnaît les mots "gris", "gros" et "gras" !
Quelques exemples :
Chaîne | Regex | Résultat |
La nuit, tous les chats sont gris | #gr[aoi]s# | VRAI |
Beurk, c'est trop gras comme nourriture | #gr[aoi]s# | VRAI |
Beurk, c'est trop gras comme nourriture | #gr[aoi]s$# | FAUX |
Je suis un vrai zéro | #[aeiouy]$# | VRAI |
Je suis un vrai zéro | #ˆ[aeiouy]# | FAUX |
Pour la dernière chaîne, elle doit commencer par une voyelle et en plus en minuscule. Or ça commence par "J" donc la réponse est FAUX.
Les intervalles de classe
Grâce au symbole "
-" (le tiret), on peut autoriser toute une plage de caractères.
Par exemple, on vient d'utiliser la classe [aeiouy]. Ok c'est pas trop long. Mais si on a [abcdefghijklmnopqrstuvwxyz] ? Tout ça pour dire qu'on veut qu'il y ait une lettre ?
Et bien, on a le droit d'écrire [a-z] et si on veut s'arrêter à la lettre "e", on écrit [a-e].
Ca fonctionne également avec les chiffres. Si on veut un chiffre entre 1 et6, on tape : [1-6].
On peut même mettre 2 plages à la fois dans une classe : [a-z0-9] signifie "N'importe que lettre (minuscule) OU un chiffre".
Bien entendu, on peut aussi autoriser les majuscules, sans passer par les options comme tout à l'heure.
[a-zA-Z0-9] est donc une façon plus courte d'écrire [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]
Quelques exemples :
Chaîne | Regex | Résultat |
Cette phrase contient une lettre | #[a-z]# | VRAI |
cette phrase ne comporte pas de majuscule ni de chiffre | #[A-Z0-9]# | FAUX |
Je vis au 21ème siècle | #ˆ[0-9]# | FAUX |
<h1>Une balise de titre HTML</h1> | #<h[1-6]># | VRAI |
Le dernier exemple est intéressant car on se dirige doucement vers la pratique. On y vérifie justement si la chaîne comporte une balise HTML de titre (<h1> ou <h2> etc… jusqu'à <h6>).
Et pour dire que j'en veux pas ?
Si on ne veut PAS des caractères qu'on énumère dans notre classe, il faut mettre le symbole "
ˆ" à l'intérieur.
 |
Je croyais que ce caractère servait à indiquer le début d'une chaîne ? |
Oui, mais si on le met à l'intérieur d'une classe, il sert à dire qu'on ne VEUT PAS de ce qui se trouve à l'intérieur de la classe.
La regex :
#[ˆ0-9]#
signifie qu'on veut que votre chaîne comporte au moins un caractère qui ne soit pas un chiffre.
Exemples :
Chaîne | Regex | Résultat |
Cette phrase contient autre chose que des chiffres | #[ˆ0-9]# | VRAI |
cette phrase contient autre chose que des majuscules et des chiffres | #[ˆA-Z0-9]# | VRAI |
Cette phrase ne commence pas par une minuscule | #ˆ[ˆa-z]# | VRAI |
Cette phrase ne se termine pas par une voyelle | #[ˆaeiouy]$# | FAUX |
ScrrmmmblllGnngngnngnMmmmmffff | #[ˆaeiouy]# | VRAI |
Les quantificateurs
Les quantificateurs, ce sont des symboles qui permettent de dire combien de fois peuvent se répéter un caractère, ou une suite de caractères.
Par exemple, pour reconnaître une adresse e-mail comme francois@free.fr, il va falloir dire : "Elle commence par une ou plusieurs lettres, elle est suivie d'un @ (arobase), suivie de deux lettres au moins, suivi d'un point, et enfin de 2 à 4 lettres (pour le .fr, .com., mais aussi .info (ça existe !)).
Cette regex, on ne va pas encore l'écrire (trop tôt), mais on voit qu'il est indispensable en général d'indiquer combien de fois une lettre peut se répéter.
Les symboles les plus courants
on doit retenir 3 symboles :
- ? (point d'interrogation) : ce symbole indique que la lettre est facultative. Elle peut y être 0 ou 1 fois.
Ainsi, #a?#
reconnaît 0 ou 1 "a".
- + (signe plus) : la lettre est obligatoire. Elle peut apparaître 1 ou plusieurs fois.
Ainsi, #a+#
reconnaît "a", "aa", "aaa", etc…
- * (étoile) : la lettre est facultative. Elle peut apparaître 0, 1 ou plusieurs fois.
Ainsi, #a*#
reconnaît "a", "aa", "aaa", etc… Mais s'il n'y a pas de "a", ça fonctionne aussi.
 |
Noter que ces symboles s'appliquent à la lettre se trouvant directement devant. On peut ainsi autoriser le mot "chien" qu'il soit au singulier comme au pluriel, avec la regex #chiens?# (fonctionnera pour "chien" et "chiens"). |
On peut donc autoriser la répétition d'une lettre. On vient de voir le cas pour "chien". Mais on peut aussi s'en servir pour une lettre au milieu du mot, comme ceci :
#bor?is#
. Ce code reconnaîtra "boris" et "bois".
 |
Et si je veux que ce soient 2 lettres ou plus qui se répètent, comment je fais ? |
Il faut utiliser des parenthèses. Par exemple, si on veut reconnaître "Ayayayayayay" (le cri de guerre de Speedy Gonzalez), on devra taper la regex suivante :
#Ay(ay)*#
. Ce code reconnaîtra "Ay" et "Ayay", "Ayayay", etc…
 |
On peut utiliser le symbole "|" dans les parenthèses. La regex : #Ay(ay|oy)*#
… renverra vrai par exemple pour "Ayayayoyayayayoyoyoyoyayoy" ! C'est le "ay" OU le "oy" répété plusieurs fois, tout simplement ! |
Egalement, on peut mettre un quantificateur après une classe de caractères (ce qui est avec les crochets !). Ainsi
#[0-9]+#
… permet de reconnaître n'importe quel nombre, du moment qu'il y a au moins un chiffre !
Quelques exemples :
Chaîne | Regex | Résultat |
eeeee | #e+# | VRAI |
ooo | #u?# | VRAI |
magnifique | #[0-9]+# | FAUX |
Yahoooooo | #ˆYaho+$# | VRAI |
Yahoooooo c'est génial ! | #ˆYaho+$# | FAUX |
Blablablablabla | #ˆBla(bla)*$# | VRAI |
La regex (
#ˆYaho+$#
) signifie que la chaîne doit commencer et finir par le mot "Yahoo". Il peut y avoir 1 "o" ou plusieurs. Ainsi "Yaho", "Yahoo", "Yahooo" etc…marchent. Mais on ne doit rien mettre avant ni après car on a indiqué que c'était un début ET une fin de chaîne avec ˆ et $.
La dernière regex autorise les mots "Bla", "Blabla", "Blablabla" etc… On s'est servi des parenthèses pour indiquer que "bla" peut être répété 0, 1 ou plusieurs fois.
Etre plus précis grâce aux accolades
Parfois on aimerait indiquer que la lettre peut être répétée 4 fois, ou de 4 à 6 fois… bref on aimerait être plus précis sur le nombre de fois où ça se répète.
C'est là qu'entrent en jeu les accolades. Il y a 3 façons d'utiliser les accolades :
- {3} : si on met juste un nombre, cela veut dire que la lettre (ou le groupe de lettres s'il est entre parenthèses) doit être répété 3 fois exactement.
#a{3}#
fonctionne donc pour la chaîne "aaa".
- {3,5} : ici, on a plusieurs possibilités. On peut avoir la lettre de 3 à 5 fois.
#a{3,5}#
fonctionne pour "aaa", "aaaa", "aaaaa".
- {3,} : si on met une virgule, mais pas de 2ème nombre, ça veut dire qu'il peut y en avoir jusqu'à l'infini. Ici, cela signifie "3 fois ou plus".
#a{3,}#
fonctionne pour "aaa", "aaaa", "aaaaa", "aaaaaa" etc…
 |
Si on fait attention, on remarque que :
- ? correspond à écrire {0,1}
- + correspond à écrire {1,}
- * correspond à écrire {0,}
|
Quelques exemples :
Chaîne | Regex | Résultat |
eeeee | #e{2,}# | VRAI |
Blablablabla | #ˆBla(bla){4}$# | FAUX |
546781 | #ˆ[0-9]{6}$# | VRAI |
Dans le prochain chapitre, on mélange tout !