Lucas Willems

LUCAS WILLEMS

Un étudiant de 27 ans passionné par les maths et la programmation

English

Tutoriel pour maîtriser les expressions régulières

Article

Les expressions régulières, ou plus communément regex (contraction de regular expression) permettent de représenter des modèles de chaînes de caractère. Ce sont des outils très puissants et très utilisés : on peut les retrouver dans de nombreux langages comme le PHP, MySQL, Javascript... ou encore dans des logiciels d'édition de code ! Cependant, si cet outil est très puissant, il est relativement difficile à appréhender au début car les expressions régulières peuvent prendre des formes de ce genre :

#^[a-zA-Z-]+@[a-zA-Z-]+\.[a-zA-Z]{2,6}$#

Cet expression régulière permettant, par exemple, de représenter toutes les adresses emails contenues dans un texte. Vous pouvez donc voir que la syntaxe est peu commune et qu'un petit tutoriel s'impose pour pouvoir comprendre et utiliser le concept des expressions régulières. Pour suivre ce tutoriel, il vous faudra utiliser un éditeur de code qui comprend les expressions régulières : si vous n'en avez pas encore un, vous pouvez télécharger et installer notepad++.

La limite des recherches classiques

Pour commencer simplement, ouvrons notre éditeur de code et collons y le texte suivant :

Bonjour et au revoir ! Je m'appelle John Doe, j'ai 27 ans, j'habite en France et travaille depuis que j'ai 20 ans. Ma passion : écrire des mots, mits, mets, mats, mat... Pour me contacter, vous pouvez envoyer un email à contact@johndoe.fr ou contact@johndoe.com ou bien m'appeler au 06 07 08 09 10. Vous pouvez aussi aller voir mon blog à l'adresse johndoe-blog.fr. Bonjour et au revoir

Puis, recherchons y, en rendant la recherche sensible à la casse (voir image précédente), le mot Bonjour. La façon la plus simple est alors de faire un simple Ctrl+F comme on en a l'habitude : pas besoin d'utiliser les regex. Suite à cette recherche, les 2 occurrences de Bonjour devraient être sélectionné. Normal.

Maintenant, imaginons que nous voulons rechercher, cette fois-ci, seulement le mot Bonjour qui se trouve au tout début du texte. Comment faire avec un simple Ctrl+F ? Ce n'est tout simplement pas possible : voici donc la limite des recherches classiques. Pour faire des recherches plus complexes, il nous faudra utiliser les regex.

Regex simple

Reprenons notre recherche du mot Bonjour du début de texte, mais cette fois, en utilisant les regex, et plus particulièrement les caractères de début et fin de chaîne.

Les caractères de début et fin de chaîne

Les caractères de début et de fin de chaîne, respectivement ^ et $, représentent, comme leur nom l'indique, le début et la fin de la chaîne. Donc, si nous retournons dans notre éditeur de code, que nous faisons de nouveau Ctrl+F, que nous activons les expressions régulières au passage et que nous entrons dans le champ de recherche la regex suivante :

^Bonjour

L'éditeur de code devrait sélectionner seulement le mot Bonjour se trouvant au début du texte. Et si nous remplaçons la regex que nous venons d'entrer par celle-ci :

revoir$

L'éditeur de code devrait sélectionner seulement le mot revoir qui se situe à la fin du texte.

Le caractères OU

Imaginons maintenant que nous voulons rechercher, dans le texte, les mots Bonjour et revoir, c'est à dire le mot Bonjour OU le mot revoir : nous devrons alors utiliser la barre verticale |. Ainsi la regex suivante sélectionnera toutes les occurrences de Bonjour et revoir :

Bonjour|revoir

Il est aussi possible de sélectionner les occurrences du mot Bonjour se trouvant au début du texte et du mot revoir se trouvant à la fin, ce qui revient à fusionner, en les séparant par une barre verticale, les 2 regex vues dans la sous partie précédente. Voici donc la regex correspondant :

^Bonjour|revoir$

Les ensembles de caractères

Passons maintenant à des regex un peu plus complexes, et beaucoup plus puissantes, en nous attardons sur les ensembles de caractères.

Les différents ensembles

Maintenant, nous voulons chercher dans notre texte, les mots motsmats et mits. Nous pourrions très bien utiliser cette regex :

mots|mats|mits

Mais il y a plus simple et ce grâce aux ensembles de caractères qui font office, en quelques sortes, de OU en plus courts et plus puissants. Un ensemble de caractère est délimité par des crochets dans lesquels se trouvent les caractères faisant parti du OU. Ainsi, la regex suivante :

m[oai]ts

est beaucoup plus succincte que la précédente et sélectionne les mêmes mots. Cette regex peut être expliciter par la phrase suivante : "Sélectionne les parties du texte où il y a un m, suivi d'un o ou d'un a ou d'un i, suivi d'un t, suivi d'un s." Les ensembles de caractères permettent aussi d'exclure des caractères grâce à l'accent circonflexe ^.  La regex suivante :

m[^oai]ts

sélectionnera, cette fois-ci, seulement le mots mets et peut être explicitée par la phrase suivante : "Sélectionne les parties du texte où il y a un m, suivi d'une lettre qui n'est ni o, ni a, ni i, suivi d'un t, suivi d'un s." Enfin, imaginons que nous voulons sélectionner tous les mots commençant par un m, suivi de n'importe quelle lettre, suivi d'un t, suivi d'un s. La regex qui nous viendrait à l'esprit serait une regex de ce type :

m[abcdefghijklmnopqrstuvwxyz]ts

La regex serait donc longue et fastidieuse à écrire, surtout que pour celle-ci, seules les minuscules ont été sélectionnées ! Heureusement, un moyen plus simple existe pour écrire de telles regex : cela s'appelle les intervalles et se note [debut intervalle-fin intervalle]. Voici quelques petites exemples :

IntervalleEquivalentTraduction
[a-z][abcdefghijklmonpqrstuvwxyz]Lettres minuscules de a à z
[A-Z][ABCDEFGHIJKLMNOPQRSTUVWXYZ]Lettres majuscules de A à Z
[0-9][0123456789]Chiffres de 0 à 9
[a-z0-9][abcdefghijklmonpqrstuvwxyz0123456789]Lettres minuscules de a à z ou chiffres de 0 à 9

Reprenons donc notre regex et utilisons les intervalles. Nous obtenons alors :

m[a-z]ts

ce qui sélectionne le mot mets en plus des mots motsmats et mits.

Les ensembles préconçus

Un ensemble préconçu est une façon très simple de représenter plusieurs intervalles. Voici quelques exemples :

EnsembleEquivalent
.Absolument n'importe quel charactère
\w[a-zA-Z0-9_]
\d[0-9]
\nUn retour à la ligne
\tUne tabulation

Ainsi, cette regex :

m\wts

sélectionnera les mêmes éléments que celle-ci :

m[a-zA-Z0-9_]ts

Les quantificateurs

Nous venons de voir qu'un ensemble de caractères permet de définir de manière très simple les valeurs possible d'un caractère. Mais qu'en est-il si l'on définir les mêmes valeurs possibles pour plusieurs caractères ? Par exemple, si l'on veut sélectionner les parties du texte où il y a un m, suivi d'un a, suivi de 3 fois n'importe quelle lettre minuscule, est-on obligé d'utiliser une regex de ce type :

ma[a-z][a-z][a-z]

Non. Il existe une méthode plus simple qui consiste à utiliser les quantificateurs : ce sont des caractères qui indiquent le nombre de répétition du caractère ou de la suite de caractère qui les précèdent. Le quantificateur, dans sa forme explicite, peut s'écrire de 4 façons :

Par exemple, la regex suivante :

[a-zA-Z]{6}

permet de sélectionner les parties du texte où il y a 6 lettres consécutives. Celle-ci :

[0-9]{2,4}

permet de sélectionner les parties du texte où il y a entre 2 et 4 chiffres consécutifs. Comme pour les ensembles de caractères, il existe aussi des quantificateurs préconçus. En voici la liste :

QuantificateurTraductionÉquivalent
*0 ou plusieurs répétitions{0,}
+1 ou plusieurs répétitions{1,}
?0 ou 1 répétition{,1}

L'échappement

Reprenons notre texte et imaginons que l'on veuille y sélectionner les noms de domaine des adresse email contact@johndoe.fr et contact@johndoe.com. La regex qui nous viendrait donc à la tête serait la suivante :

johndoe.[a-z]{2,3}

Et malheureusement, ce n'est pas exactement celle-là, car même si les 2 noms de domaine sont bien sélectionnés, la chaîne de caractère johndoe-blo l'est aussi.

Ce problème vient alors du point présent dans la regex car rappelez-vous que le point est un ensemble de caractères préconçus qui représente n'importe quel caractère : il peut représenter un a, un 2 mais aussi un tiret. Pour faire comprendre que le point présent dans la regex est bien un point et non pas un ensemble de caractères, il nous faut échapper le point avec le caractère d'échappement qui est le backslash \. Ainsi la regex correcte est la suivante :

johndoe\.[a-z]{2,3}

Cet échappement n'est pas seulement valable pour le point, mais pour tous les caractères qui ont, de base, une valeur différente que celle habituelle. En voici la liste : ^ $ \ | { } [ ] ( ) ? # ! + * .

Mise en pratique

Maintenant que vous possédez d'assez bonnes bases pour comprendre et utiliser les expressions régulières, il est temps de sortir des simples recherches dans l'éditeur de code et de passer à des cas plus pratiques par l'étude de 2 exemples d'utilisation des regex dans le langage de programmation PHP.

Vérifier si un numéro de téléphone est correcte

Le premier exemple consiste à vérifier si un numéro de téléphone est correcte. Pour se faire, imaginons que l'on ait créé, dans une page form.html, un formulaire contenant plusieurs champs dont un nommé numero dans lequel l'utilisateur saisirait son numéro de téléphone. Une fois le formulaire complété, l'utilisateur cliquerait sur Valider et enverrait ses données vers une page traitement.php dans laquelle on les vérifierait avant de les entrer dans une base de donnée. La partie nous concernant donc est de vérifier la validité de ce numéro de téléphone.

Commençons par la regex et admettons qu'elle permette de rechercher les numéros français, commençant par 0 ou +33 et possédant 9 chiffres sans compter le 0 ou +33. Ainsi, voici une écriture possible de celle-ci :

(0|\+33)[1-9]( *[0-9]{2}){4}

Peut-être venez vous de remarquer l'apparition de parenthèses. Celles-ci permettent de créer des suites (ou groupe) de caractères et de les stocker en mémoire. Ainsi, si, par exemple, le caractère précédent un quantificateur est en fait une suite de caractère, ce n'est pas le caractère précédent mais la suite de caractère qui sera répétée plusieurs fois. Voici maintenant ce que donne le code PHP entier comprenant la regex :

<?php
$numero = $_POST['numero'];
if (preg_match('#(0|\+33)[1-9]( *[0-9]{2}){4}#', $numero)) {
    echo "Le numéro de téléphone entré est correct.";
    // On peut ajouter le numéro à la base de donnée
} else {
    echo "Le numéro de téléphone entré est incorrect.";
    // On ne peut pas ajouter le numéro à la base de donnée
}
?>

Là aussi, 2 remarques sont à faire sur ce code :

Détecter les liens et les rendre cliquable

Passons maintenant au second exemple qui est aussi assez pratique et qui consiste à détecter la présence d'url dans un texte et à les convertir en liens cliquables. Imaginons que le contexte soit le même que celui de l'exemple précédent et que, en plus de saisir son  numéro de téléphone, l'utilisateur saisisse du texte pouvant contenir des url. Cette fois-ci, la partie nous intéressant consiste à rechercher ces url dans le texte et à les transformer en liens cliquables.

Pour commencer, définissons la forme que peuvent prendre les url. Celles-ci commenceront par http ou https, seront suivies de ://, suivies des sous-domaines et du nom de domaine, ne contenant que des chiffres, lettres, tirets et points et une extension de 2 à 4 caractères, suivies enfin du chemin d'accès (facultatif) comprenant tous les caractères sauf l'espace. Voici donc une regex possible pour rechercher des url :

https?://[a-zA-Z0-9\.-]+\.[a-zA-Z]{2,4}(/\S*)?

où \S signifie tout sauf l'espace et où, petit rappel, le ? permet de rendre facultatif le caractère le précédent. Voici maintenant le code entier :

<?php
$message = $_POST['message'];
$message = preg_replace(
    '#https?://[a-zA-Z0-9-\.]+\.[a-zA-Z]{2,4}(/\S*)?#',
    '<a href="$0" target="_blank">$0</a>',
    $message
);
echo $message;
?>

Comme dans l'exemple précédent, vous avez pu remarquer l'apparition d'une nouvelle fonction, preg_replace, permettant de remplacer des caractères dans un texte en utilisant les regex. Celle-ci prend en paramètre la regex, la chaîne de remplacement, et le texte contenant les chaînes à remplacer, et retourne le texte une fois les remplacements effectués.

Vous avez aussi pu remarquer la présence de $0 (appelées référence) dans le second paramètre de cette fonction. Ces références correspondent au texte satisfaisant toute la regex. D'autres références existent comme $1, $2, $3... qui, elles, correspondent au texte satisfaisant la première parenthèse de la regex, la seconde, la troisième... Si vous n'avez pas bien compris le fonctionnement de ces références, je vous conseille de bien regarder les exemples données dans la documentation PHP de cette fonction.

Le tutoriel touche à sa fin. J'espère qu'il vous aura permis de comprendre le fonctionnement des expressions régulières et d'en acquérir de bonnes bases. N'hésitez pas aussi à me donner votre avis en laissant un commentaire ou en m'envoyant un mail.

Recherche

Voici les recherches relatives à cette page :

Commentaires

Qu'en pensez-vous ? Donnez moi votre avis (positif ou négatif) pour que je puisse l'améliorer.