Expressions régulières, utilisation de la fonction preg en PHP

Un article de Wiki SOS-ADMIN.


Cet article est issu de mon blog disponible ici


Sommaire

Introduction

Les expressions régulières ou rationnelles sont utiles à toute personne désirant faire des recherches complexes dans un fichier ou un texte. J'entends par complexes, une recherche impossible par toutes fonctions classiques de traitement de chaines de caractères.

Les regex sont couramment utilisées quand on fait de l'URL Rewritting ou lorsque l'on veut modifier le contenu d'un fichier. Par exemple, j'utilise la synthaxe dite 'Wiki' pour écrire ce texte et vous le visualiser en (x)HTML grâce aux expressions régulières.


Vous pouvez lire la définition sur Wikipédia La synthaxe que j'utiliserai dans la suite sera la suivante :

#pattern#option

pattern représente le motif à examiner dans une chaine et option représente les différentes options de recherches.


Vocabulaire

Les mots définis ci dessous seront utilisés par la suite dans ce billet, c'est pourquoi je vais essayer de bien expliquer ce vocabulaire afin que vous puissiez lire la suite de l'article sans trop de problèmes. Excusez mes approximations dans le jargon mathématique, la théorie des ensembles est très lointaine et à cette époque, je ne suivais pas forcement ces cours avec l'aciduité requise.

  • lettre : Plus petite entité permettant de créer un motif
  • alphabet : Ensemble *E* de toutes les lettres
  • classe : un sous ensemble de l'alphabet
  • motif ou pattern : combinaison de lettres et de classes permettant d'avoir une réprésentation mathématique d'une structure
  • méta caractère : lettre ayant une signification plus complexe que celle couramment reconnue


Les méta caractères

Il existe différents méta-caractères, que je vais lister de suite avec je l'espère une certaine logique.

  • ^ : signifie la négation quand celui ci est utilisé dans une classe
  • ^ : signifie le début d'un chaine (ex: le motif ^to reconnait "toit" mais pas "moto")
  • $ : signifie la fin d'une chaine (ex: le motif to$ reconnait "moto" mais pas "toit")
  • | : correspond à l'opérateur logique OU (le motif a|b reconnait un mot avec la lettre a ou la lettre b)
  • \ : caractère d'echappement, il permet de reconnaitre tous caractères su-cités
  • . : représente n'importe quel caractère (excepté le retour chariot \n et/ou \r)
  • * : méta caractère indiquant que le motif peut être répeter 0 ou n fois
  • + : méta caractère indiquant que le motif peut être répeter 1 ou n fois
  • [ et ] : délimiteur de classe
  • { et } : délimiteur du nombre de répetition d'un motif
  • - : permet de délimiter un intervale dans une classe (exemple [a-m] => tous les lettres minuscules comprises entre a et m compris. Si '-' est en début de classe celui-ci signifie le tiret

Classes de caractère

Une classe de caractères est définie comme ceci :

[liste de mes caractères]

Un chiffre se défini donc :

[0|1|2|3|4|5|6|7|8|9]

Nous avons vu dans le précédent point que nous pouvions faire des intervales grace aux tirets. Un chiffre peut donc s'écrire [0-9] ce qui est beaucoup plus simple à écrire.

Il existe différentes classes de caractères prédéfinies car, par exemple, il serait fastidieux d'écrire une classe représentant tous les caractères de l'alphabet à chaque fois que l'on veut l'utiliser.


Liste des différentes classes :

  • [ [:alpha:] ] : tous les caractères alphabétiques => [a-zA-Z]
  • [ [:alnum:] ] : tous les caractères alphanumériques => [a-zA-Z0-9]
  • [ [:digit:] ] : tous les caractères numériques => [0-9]
  • [ [:xdigit:] ] : tous les caractères numériques héxadécimaux => [0-9A-F]
  • [ [:blank:] ] : tous les caractères d'espacement, soit l'espace et la tabulation
  • [ [:ascii:] ] : tous les caractères dont le code ascii est compris entre 0-127
  • [ [:upper:] ] : tous les caractères majuscules
  • [ [:lower:] ] : tous les caractères minuscules

ne pas prendre en compte les espaces entre doubles crochets

Cependant certaines de ces classes on encore était simplifiée :

  • \d correspond à un chiffre
  • \w correspond à un caractère pouvant former un mot
  • \s correspond à un espace

Les options de recherches

  • i , permet de faire une recherche insensible à la casse (majuscule et minuscule non distincte)
  • m, permet d'effectuer une recherche multiligne
  • U, permet d'utiliser les répétitions non gloutonne (par ex. le motif '#"(.*)"#U' reconnaitra toto, titi et tutu dans la chaine "toto","titi","tutu" mais le même motif sans le U reconnaitra toto","titi",tutu ce qui n'est par forcement ce que l'on recherche.


D'autres options de recherches existes, je les detaillerais un peu plus tard.


Comment réaliser un motif

Je pense qu'une des meilleures manières de comprendre la construction d'un motif est encore de le montrer par l'exemple.


Nous allons créer le motif permettant de reconnaitre n'importe que adresse IP.

Pour cela, il faut déjà définir ce que c'est :

adresse IP : suite de 4 nombres allant de 0 à 255 séparé par un point

Notre motif devra donc reconnaitre toute chaine allant de 0.0.0.0 à 255.255.255.255

Nous avons notre définition linguistique simplifiée d'une adresse IP, nous allons la traduire de façon rationnelle.

adresse IP : [0-255].[0-255].[0-255].[0-255]

On s'aperçoit avec cette définition qu'un motif réapparait 3 fois, ce qui nous permet de simplifier encore plus notre définition.

adresse IP : [0-255](.[0-255]){3}

Nous ne pouvons pas simplifier plus cette définition dans l'état actuel des choses, essayons de d'écrire le motif correspondant :

[0-255] peut s'écrire (0|1|2|...|255) ce qui est long à écrire mais ce qui est surtout simplifiable.

[0-255] = 0 + [1-199] + [200-249] + [250-255]


Nous allons donc créer les patterns [1-199], [200-249], [250-255]

[1-199] => (1([0-9]{0,2}))

soit  :

toute suite de 1 à 3 chiffres commencant par 1 suivit de 0 ou 1 ou 2 fois un chiffre allant de 0 à 9, c'est bien ce que l'on recherchait.


[200-249] => (2([0-4])([0-9]{0,1})) 

soit :

tout nombre ayant pour centaine le chiffre 2, un chiffre de dizaine allant de 0 à 4 et un chiffre d'unité allant de 0 à 9, le contrat est donc remplit.


[250-255] => (25[0-5])

soit :

pour aller de 250 à 255, il faut bien 25 suivit d'un chiffre allant de 0 à 5


[0-255] => (0|(1([0-9]{0,2}))|(2([0-4])([0-9]{0,1}))|(25[0-5]))

soit :

hum, c'est difficile à lire, d'où l'utilité de bien morceller son cheminement de création de pattern ;-)


Je vous laisse le loisir de simplifier encore ce motif.


Nous avons vu précédement qu'une adresse IP pouvait se définir comme cela, [0-255](.[0-255]){3}, nous pouvons désormais écrire notre motif de reconnaissance d'une adresse IP.

Motif adresse IP :

(0|(1([0-9]{0,2}))|(2([0-4])([0-9]{0,1}))|(25[0-5]))(.(0|(1[0-9]{0,2}))|(2([0-4])([0-9]{0,1}))|(25[0-5]))){3}

Le motif se lit sur une seule ligne, je l'ai morcelé pour une question de lisibilité.

Voilà, grace à ce motif, vous pouvez chercher n'importe quelle adresse IP contenu dans une chaine de caractères, ce qui est beaucoup plus simple que de rechercher les 256^4 adresses soit les 4 294 967 296 possibilités avec une fonction pour chaque possiblité, ce qui est beaucoup plus fastidieux à écrire ;-)

Les fonctions preg de PHP

En cliquant sur chaque fonction , vous verrez la page du manuel PHP correspondant. J'écrirais peut être un petit explicatif de chaque fonction mais le manuel PHP me semble le plus approprié :


Exemple d'expression réguliere

Adresse IP :

(0|(1([0-9]{0,2}))|(2([0-4])([0-9]{0,1}))|(25[0-5]))(.(0|(1([0-9]{0,2}))|(2([0-4])([0-9]{0,1}))|(25[0-5]))){3} 


N° téléphone français :

(0|\+\(33\))[1-68](([-\. ])\d{2}){4}

Conclusion

Les expressions régulières sont des outils puissants si elles sont bien utilisées.

Des fautes se sont peut-être glissées dans l'article, n'hésitez pas à me les soumettre tout comme vos motifs pratiques et/ou régulièrement utilisés.

De plus, si vous voyez des choses a dire en plus, n'hésitez pas à le soumettre

Je recherche quelqu'un pour rédiger une partie sur les assertions

Sources

Le livre PHP 5 edité par Eyrolles

Un cours de Nicolas Chambrier sur les expressions rationnelles

Wikipédia

D'autres sources dont j'ai perdu les références (qu'ils m'excusent)

Ma propre experience ;)


Auteur de l'article


Nicolas Desaleux