Les regex

Un chapitre important s’il en est, mais relativement complexe à aborder, est celui des expressions rationnelles.

L’expression rationnelle

Expression rationnelle
Une expression rationnelle (ou expression régulière par traduction de l’anglais regular expression) est en informatique une chaîne de caractères que l’on appelle parfois un motif et qui décrit un ensemble de chaînes de caractères possibles selon une syntaxe précise. Les expressions rationnelles sont issues des théories mathématiques des langages formels des années 1940. Leur puissance à décrire des ensembles réguliers explique qu’elles se retrouvent dans plusieurs domaines scientifiques dans les années d’après-guerre et justifie leur adoption en informatique. Les expressions rationnelles sont aujourd’hui utilisées par les informaticiens dans l’édition et le contrôle de texte ainsi que dans la manipulation des langues formelles que sont les langages de l’informatique. (source Wikipedia)

En clair, une regex est un «modèle» qui permet de valider des portions de texte, ou de limiter la saisie. Dans notre cas, nous allons utiliser les expressions rationnelles pour transformer les urls. C’est ce qu’on appelle le principe de réécriture d’url.

La plupart des CMS le font automatiquement en écrivant eux-même le fichier de réécriture (.htaccess), mais nous allons mettre les mains dans le cambouis et le faire nous-même !

Une fois que les expressions rationnelles n’auront plus aucuns secrets pour vous, vous pourrez plus facilement contrôler les formulaires et autres redirections.

Pour mieux appréhender les regex, nous allons utiliser ce site pour les exercices : http://regex101.com/

Posix ou PCRE ?

Il y a deux types d’expressions rationnelles. Le serveur Apache, avec ses fichiers de configuration htaccess, et PHP utilisent notamment la norme POSIX. La norme POSIX est la norme « standard », simple mais moins performante. L’autre norme, PCRE, plus utilisée dans les langages évolués tels que PERL (et PHP…) est plus complexe mais offre beaucoup plus de possibilités, et est plus rapide que sa consœur.

Nous verrons ici la norme PCRE, car si vous maitrisez la PCRE, vous serez un expert dans la POSIX !

Pour les pros du php, les fonctions utilisant la norme PCRE commencent par preg_.

Pour commencer

Commençons par préparer le terrain de jeu. Le but d’une regex est de rechercher un motif particulier dans du texte. Nous allons donc taper un texte avec plein de motifs. Tapez le texte suivant dans la zone String du site regex101.

J’aime les animaux. Tous les animaux.

Les chiens, les chats, les chevaux, les vaches et les veaux.

Même les chenilles, car elles présagent de beaux papillons.

CHIEN et CHAT

Le chien et le chat sont des animaux domestiques.

On peut en adopter dans des chenils.

Par contre, on ne trouvera jamais de vache dans un chenil.

En regardant de près, on pourra parfois y trouver une chenille, mais on ne pourra pas l’adopter !

Dans ce chenil, il y a un gros chat gris qui aime manger du gras.

Délimiteurs

L’utilisation d’un délimiteur est propre à PCRE. Le rôle d’un délimiteur est de séparer dans l’expression rationnelle le motif des options. Et c’est là que POSIX est moins puissant : il ne dispose pas des options. Les délimiteurs en PCRE sont multiples, mais en général on utilise le bang (#, ou dièse) ou le slash (/, ou barre oblique). Remarquez que regex101 affiche des slash.

Essayons de sélectionner dans le texte le mot chien. Pour sélectionner du texte, il faut écrire un motif. Un motif est une suite de règles à suivre. Ici, si on veut surligner les occurrences de “chien”, nous écrirons le motif #chien# (ou /chien/)

Combien d’occurrence du mot #chien# sont surlignées ? 1! Et encore, il surligne le premier chiens. Or, le mot termine par “s”.

Pourquoi fait-il ça ?

La raison est simple. N’oublions pas que c’est une machine, et qu’elle suit la règle du motif. On lui dit : “trouve le premier ensemble de lettres dans cet ordre : c, h, i, e, n”. Il l’a trouvé. Qu’il y aie un “s” en plus, il s’en fiche, puisque le motif est respecté, las caractères supplémentaires sont ignorés.

Dans notre cas, on “triche” un peu, car le système nous demande une phrase. Or, nous lui avons passé un texte complet. Pour que le système analyse tout le texte, nous allons jouer avec des options.

Les options

Les options sont la puissance de PCRE. Ce sont des codes qui modifient la règle de base. Les options se placent derrière le motif.

Attention, il n’y a pas d’options en POSIX.

Les options les plus utilisées sont :

  • g : modificateur global. Oblige le système à analyser tout le texte, ne s’arrête pas à la première occurrence.
  • m : multi lignes : ajoute des tokens spéciaux à chaque ligne (voir plus loin).
  • s : single line : ne prend qu’une ligne.
  • u : unicode : gère l’unicode dans le pattern.
  • i : insensible à la casse : ignore les majuscules et les minuscules.

Il y en a d’autres, non vues ici. Notez aussi que les options sont cumulables.

Réessayons de surligner nos chiens. ajoutons l’option g à notre motif. Tapons #chien#g. Combien d’occurrences avons-nous ? 2! Cette fois-ci, il a bien pris les chiens, sauf celui en majuscules. Avec #chien#gi, on est bon.

Les ancres

Tentons de surligner les mots terminant par –aux. Le pattern sera : #aux#g. Il y a 6 occurrences dans le texte. Maintenant, comment détecter les phrases qui terminent par –aux ?

Nous allons ajouter une ancre. L’ancre est un caractère spécial qui “bloque” la sélection. L’ancre de fin de phrase est le $ (ou le \Z). Pour surligner les phrases qui terminent par –aux, nous allons écrire #aux$#g.

Je sais, vous me direz aucune occurrence, et c’est normal ! les phrases ne terminent par –aux, mais par un point ! Cela ne colle pas au motif. De plus, le point (.) est un caractère spécial, on ne peut l’utiliser tel quel. Nous verrons comment rectifier cela plus tard.

Sélectionnons à présent les phrases qui commencent par les. L’ancre de début de texte est le circonflexe ^ (ou le \A). le pattern sera donc #^les#gim (le m pour analyser chaque ligne)

Revenons sur nos #chien#gi pour voir une nouvelle ancre. Pour sélectionner des mots entiers, nous allons utiliser l’ancre des fin de mots \b. Tapez #chien\b#gi pour voir le résultat.

Les classes

Corsons un peu la difficulté. Sélectionnons les chats et les chiens. La méthode simple et efficace est d’utiliser le ou (|) #chien|chat#gi (7 occurrences).

Comment puis-je sélectionner les mots gras, gros et gris ? Remarquez qu’ils ont un motif bien précis : chacun de ces mots commence par gr, puis une voyelle, puis un s. Pour réussir, nous allons utiliser une classe. Une classe est un groupe entre crochets, qui permet de délimiter une étendue. Testons ceci : #gr[aio]s#gi.

Entre crochets, nous indiquons “soit a, soit i, soit o”. Les classes sont aussi intéressantes pour délimiter des plages. Par exemple, la classe [a-z] indique un caractère en minuscule. [A-Z] indique un caractère en majuscules (et un seul !). La classe [a-zA-Z] indique un caractère minuscule ou majuscule. Enfin [0-9] indique un chiffre entre 0 et 9.

Et si je souhaite trouver une consonne ? Eh bien, il faudra écrire le motif suivant : [bcdfghlklmnpqrstvwxzBCDFGHJKLMNPQRSTVWXZ]. C’est long… Mais on peut le raccourcir un peu en utilisant les intervalles : [b-df-hj-np-tv-xzB-DF-HJ-NP-TV-XZ].Plus court, certes, mais plus difficile à lire…

Il est plus simple de dire de sélectionner ce qui n’est pas une voyelle ! [^aeiouAEIOU0-9]. Ici, le circonflexe agit comme une négation.

Les quantificateurs

C’est chouette de pouvoir sélectionner n’importe quel caractère, mais si j’en veux plusieurs ? Pour y parvenir, nous devons utiliser des quantificateurs.

Sélectionnons tous les mots contenant chien. Pour accepter le singulier comme le pluriel, nous allons utiliser le quantificateur ?. Ce quantificateur signifie 0 ou 1 caractère requis, et s’applique au dernier caractère. #chiens?#gi (3 occurrences).

Sélectionnons à présent tous les mots, singulier ou pluriel, contenant chenil. D’instinct, nous écrirons #chenils?#gi. Oui, mais les chenilles ? Le motif exact sera #chenil(le)?s?#gi.

En plaçant des parenthèses, nous créons un groupe. Dans ce motif, nous indiquons que le mot commence par chenil, avec 0 ou 1 groupe “le”, puis 0 ou 1 s. Nous verrons plus loin que les parenthèses permettent bien plus.

Plus dur ? Sélectionnons tous les mots commençant par ch- ! Ici, nous allons utiliser le quantificateur *. * signifie 0 à n fois le caractère.

Tapons notre motif : #ch[a-z]*#gi. L’étoile porte sur l’intervalle [a-z], donc nous répétons cet intervalle plusieurs fois. Notez 14 occurrences. Avec des demi vaches !

Pour rectifier le tir, nous devons modifier notre règle avec \b, puisque nous voulons des mots ! #\bch[a-z]*#gi !

Une alternative à l’étoile est le plus (+). Un + signifie au moins 1 à n caractère.
Plus dur encore ? Sélectionnez les mots commençant par ch- de moins de 6 lettres !

Allez, je vous aide : nous allons utiliser une structure de répétition. Cette structure de répétition utilise des accolades, avec le nombre de répétitions. Par exemple, #ch[a-z]{4}\b#gi indique que le mot commence par ch, puis qu’il y a exactement 4 caractères de l’intervalle. Pour donner une répétition plus vague, nous allons indiquer la borne minimale et maximale entre les accolades : #ch[a-z]{1,4}\b#gi. Ce qui indique un mot (\b) commençant par ch, suivi de 1 à 4 caractères entre a et Z. Donc les mots commençant par ch- de moins de 6 lettres !

Vous pouvez omettre la borne maximale des accolades. En tapant {4,}, vous indiquez une répétition de minimum 4 caractères.
Les méta séquences

Les méta séquences sont des codes ayant une symbolique spéciale. Nous en avons utilisés quelques uns jusque ici. Ce sont les ^,$ et \b notamment. La plupart des méta séquences sont échappées par le caractère \, mais pas toutes :

  • Nous l’avons vu, ^ signifie négation ou début de texte;
  • $ signifie fin de texte;
  • . signifie n’importe quel caractère ;
  • ?  signifie 0 ou 1 répétition ;
  • * signifie 0 à n répétitions ;
  • + signifie 1 à n répétitions ;
  • \ permet de transformer un méta en caractère inoffensif.

Souvenez-vous, je vous ai dit plus haut que si nous voulions surligner les phrases terminant par –aux, il fallait tricher un peu, car  les phrases finissaient par un point. Et un point signifie n’importe quel caractère, sauf un point ! Pour que celà marche, notre expression rationnelle doit s’écrire #aux\.#gm. Il faut échapper le point pour le traiter comme un caractère normal.

Exercice

Essayez de résoudre ce problème : comment valider (donc surligner) un n° de gsm dans un formulaire ? Bien entendu, nous savons déjà que les caractères alpha numériques sont interdits. Vous devez valider les n° de gsm belges, qui peuvent s’écrire soit +32 47x xxx xxx, soit 0047x xxx xxx, soit 047x xxx xxx. Sans rien devant ni derrière. De plus, les préfixes des gsm sont entre 0470 et 0490.

Je vous aide encore un peu : ne pensez pas à l’expression rationnelle entière ! Commencez par valider les parties au fur et à mesure !

solution

La solution est : ^((\+|00)32[-\. ]?|0)4([7-9][0-9])([-\. ]?[0-9]{3}){2}$

De la même manière, écrivez le motif de validation d’une adresse mail.

solution

La solution est : ^[a-zA-Z0-9_-]*@[a-zA-Z0-9_-\.]\.[a-zA-Z0-9]{2,3}$

Substitution

Pour le moment, nos expressions rationnelles ne nous servent qu’à sélectionner du texte. Mais nous pouvons nous en servir pour substituer des éléments. C’est notamment ce mécanisme qui permet de faire de la réécriture d’url.

Essayons de transformer sport/110, ou art/120 en sport.php?id=110 et art.php?id=120.

Je vous ai dit plus haut que les parenthèses avaient une fonction supplémentaire. En effet, un couple de parenthèse indique que l’on va créer un groupe pouvant être réutilisé plus tard si nécessaire.

Sachant ça, nous pouvons déduire un motif : il faut créer un groupe reprenant la partie avant le / et un groupe reprenant la partie après le /.

Notre expression rationnelle sera donc : #^([^0-9]*)\/([0-9]*)# (Pour les petits malins qui ont lu les astuces de regex101, vous pouviez écrire aussi #([\w]*)\/([\d]*)#). Pour appliquer la substitution (en php, avec preg_replace()), nous allons récupérer les groupes avec le symbole $, suivi d’un chiffre dans l’ordre d’apparition des parenthèses.

Notre substitution sera : $1.php?id=$2

Print Friendly, PDF & Email