Vous êtes sur la page 1sur 29

Tutoriel Expressions rgulires PCRE

Page 1 sur 29

A c c ue i l | Tu to ri al | Te s te ur ( F r) | T es t er ( E n ) | M an P CR E ( E n ) |

Les expressions
rgulires PCRE (Perl
Compatible Regular
Expressions)
Remarques prliminaires
Ce tutoriel est destin aux dveloppeurs
qui matrisent dj, un tant soit peu, la
syntaxe de base des regex et des fonctions
PHP qui les utilisent. Il mettra principalement l'accent sur diffrentes
mthodes d'optimisation. Pour une prsentation dtaille de la syntaxe
lmentaire, il existe de nombreux tutoriaux sur le net notamment regularexpressions.info ou expreg.com.
Les remarques qui vont suivre sur l'implmentation des PCRE par PHP ainsi
que les conseils d'optimisation des motifs valent pour tous les langages qui
utilisent les regex PCRE (Perl, bien sr, mais aussi Python, Java, .NET etc...),
avec des petites variations mineures. Tous ces langages partagent un mme
moteur, de type NFA (Nondeterministic Finite Automation). MySQL, awk et
egrep, par contre, utilisent un autre type de moteur (DFA - Deterministic
Finite Automation) plus simple, moins puissant mais plus rapide pour les cas
(trs) simples, voir simplistes ("Talking about DFA matching is very boring!"
Jeffrey Friedl).

Les bibliothques d'expressions rgulires de PHP

Le support des expressions rgulires dans PHP est assur par deux
bibliothques: POSIX et PCRE.
A premire vue, les fonctions PCRE (preg_match...) ne diffrent pas
beaucoup des fonctions POSIX (ereg....). Le prototype des fonctions ainsi
que la syntaxe des motifs se ressemblent fort. Mais c'est premire vue
seulement!
Les fonctions POSIX telles que ereg ou
ereg_replace sont obsoltes partir de la version
PHP 5.3.0 et leur utilisation dans vos scripts
produira une erreur.
Il ne sera, ds lors, question dans ce tutoriel que d'optimisation des
fonctions PCRE.

Comment optimiser l'utilisation des expressions


rgulires.

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 2 sur 29

Convention typographique
Quantificateur et gourmandise
Rfrences arrires
Les parenthses non capturantes
Remplacement avec "callback"
Les classes de caractres.
Les assertions simples
Choix des options:
i - insensible la casse
s - le point (dot) reconnat le retour ligne
m - multilignes
e - valuation de code php
U - ungreedy: quantificateurs non gourmands
x - Pour commenter les motifs
A, D, S, X - autres options moins courantes
Les assertions positive/ngatives avant/arrires
Les masques conditionnels.
donnes binaires
Optimisation et astuces
Utilisez le dot avec parcimonie
Supprimez les parenthses capturantes inutiles
Optimisez les alternatives.
Scindez vos regex trop complexes
Supprimez les options inutiles
Choisissez les fonctions callback appropries
Ancrez vos motifs
Testez et chronomtrez
man PCRE.
Quelques liens.

Convention typographique

Afin de distinguer les concordances (match) des captures, le code couleur


suivant sera utilis dans le texte:
Le texte en concordance avec le motif sera
surlign et les captures sont encadres de lignes
rouges . Le texte non concordant restera non
surlign.

La gourmandise est un vilain dfaut

Certes, mais pas dans le cas des expressions rationnelles, ce comportement


est mme recherch dans certains cas. Les quantificateurs +, * ou {1,} et
{0,} sont gourmands par dfaut dans les PCRE.

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 3 sur 29

Exemple: si on cherche capturer l'url contenue dans l'ancre suivante


<a href="http://ceci-est-une-url.com">lien</a>
Dans ce motif preg_match('#<a href=(.*)>#', $txt, $out) la fonction
capturera tout jusqu'au dernier > rencontr. Voyez plutt:
<a href= "http://ceci-est-une-url.com">lien</a >
Par contre, dans les PCRE on peut demander au dot de s'arrter au premier
> rencontr grce au point d'interrogation.
Ainsi, la regex suivante preg_match('#<a href=(.*?)>#', $txt, $out) ,
retournera ceci:
<a href= "http://ceci-est-une-url.com" >lien</a>
Dmo
Oui, oui, je sais qu'ici on aurait pu utiliser utilement une classe ngative de
caractres telle que '[^>]*' pour obtenir le mme le rsultat plus
rapidement encore (voir discussion plus bas), mais c'est juste pour
l'exemple!
retour menu principal

Les rfrences arrires (back reference)

Dans un motif PCRE, on peut insrer une rfrence arrire. C'est dire qu'on
peut capturer quelque-chose dans un texte et l'utiliser plus loin dans le motif
comme rfrence. Une utilisation approprie des rfrences arrires permet
de construire des motifs intelligents.
Exemple, dans la chane :
<i>Texte italique</i> texte normal <b> et texte
gras</b> fin du texte.
Imaginons que nous souhaitions capturer tout ce qui est contenu dans les
balises <b>ou <i>,
La regex suivante est une des solutions possibles preg_match_all('#<
([ib])>(.*?)</\1>#', $txt, $out)
La premire parenthse capturante mmorisera le i ou le b des balises
ouvrantes <b>ou <i> et l'utilisera plus loin dans les balises fermantes
</b>ou </i> grce au motif </\1> o \1, la rfrence arrire, sera gal b
ou i selon ce qui aura t captur plus avant. Rsultat:

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 4 sur 29

< i > Texte italique </i> texte normal


< b > et texte gras </b> fin du texte.
Et le tableau de sortie $out contiendra les lments suivants:
Les matches $out[0]=>
[0]=> <i>Texte italique</i>
[1]=> <b> et texte gras</b>
Capture $out[1]=>
[0]=> i
[1]=> b
Capture $out[2]=>
[0]=> Texte italique
[1]=> et texte gras
Dmo
Chaque parenthse capturante rencontre stockera la capture dans une
rfrence arrire numrote en fonction de la position de la parenthse dans
le motif. Ainsi avec le motif #((A)BCD)# on retrouvera ABCD dans la
rfrence arrire \1 et A dans \2 et ainsi de suite. Il peut y avoir jusqu' 99
rfrences arrires. Le jour o vous vous retrouvez avec 99 groupes de
parenthses capturantes, vous avez du souci vous faire pour l'optimisation
de votre motif!
Dans certains cas, il peut s'avrer intressant de ne pas incrmenter les
rfrences arrires pour en faciliter le traitement. Dans l'exemple qui suit,
vous souhaitez capturer tous les jours de la semaine dans ce texte:
Le premier jour de la semaine est le lundi suivi
par mardi, le mercredi puis le jeudi qui prcde
le vendredi.
Dans le but d'viter une numration de tous les jours de la semaine dans
votre motif, vous pourriez crire: #((lun|mar|jeu)|(mer)cre|(ven)dre)
di\b# ce motif aura 4 groupes de captures puisqu'il y a 4 groupes de
parenthses avec un niveau d'imbrication. Voyez:
Capture [1]=> array
[0]=>lun
[1]=>mar
[2]=>mercre
[3]=>jeu
[4]=>vendre
Capture [2]=>
[0]=>lun
[1]=>mar
[2]=>
[3]=>jeu

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 5 sur 29

[4]=>

Capture [3]=>
[0]=>
[1]=>
[2]=>mer
[3]=>
[4]=>
Capture [4]=>
[0]=>
[1]=>
[2]=>
[3]=>
[4]=>ven
Dmo
La version Perl 5.10 a introduit la possiblilit denepas incrmenter la
numrotation des captures dans unesried'alternatives (Duplicate
Subpattern Numbers). Il est ds lors possiblededonner lemmenumro de
captureaux parenthsecapturantes. A cettefin, lemotif ci dessus s'crira: #
(?|(lun|mar|jeu)|(mer)cre|(ven)dre)di\b#. Remarquez le ?| dela
premireparenthse. Cemotif produira un seul groupedecapture:
Capture [1]=>
[0]=>lun
[1]=>mar
[2]=>mer
[3]=>jeu
[4]=>ven
Il est maintenant trs faciled'utiliser cetteuniquerfrencearrirepour
faireun remplacement par exemple. Voyez dans letesteur cequedonnepar
exemple:
preg_replace('#(?|(lun|mar|jeu)|(mer)cre|(ven)dre)
di\b#', '\1', $texte)
retour menu principal

Les parenthses non capturantes

Les parenthses sont principalement utilises pour capturer unesriede


caractres correspondant un motif. Dans certains cas, elles seront utilises
pour dlimiter les alternatives ou bien unechanealternative. Ainsi dans le
textesuivant:

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 6 sur 29

Astuce : PHP supporte aussi des expressions


rationnelles compatibles Perl, avec l'extension
PCRE functions. Ces fonctions supportent des
recherches non-gourmandes, des assertions, des
sous-masques conditionnels et toute une gamme de
fonctionnalits absentes des expressions
rationnelles POSIX.
Admettons qu'on cherche capturer tous les mots prcds de 'des ou 'de',
on pourrait faire preg_match_all('#\b(le|les|la|de|des|du)\s(\w+)
\b#', $txt, $out) . Ce qui donne:
Astuce : PHP supporte aussi des expressions
rationnelles compatibles Perl, avec l'extension
PCRE functions. Ces fonctions supportent
des recherches non-gourmandes, des assertions ,
des sous -masques conditionnels et toute une gamme
de fonctionnalits absentes des expressions
rationnelles POSIX.
Et le tableau de rsultat $out retournera
Matches [0]=>
[0]=> des expressions
[1]=> des recherches
[2]=> des assertions
[3]=> des sous
[4]=> de fonctionnalits
[5]=> des expressions
Captures [1]=>
[0]=> des
[1]=> des
[2]=> des
[3]=> des
[4]=> de
[5]=> des
Captures [2]=>
[0]=> expressions
[1]=> recherches
[2]=> assertions
[3]=> sous
[4]=> fonctionnalits
[5]=> expressions
On voit que les parenthses du groupe de l'alternative ont effectivement
captur tous les dterminants (de, des etc...). Mais seul le deuxime groupe

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 7 sur 29

capturant, celui de $out[2], nous intresse. Par souci de clart, mais surtout
d'efficacit, on peut rendre les premires parenthses non capturantes en
rajoutant simplement ?: aprs la parenthse ouvrante: preg_match_all
('#\b(?:le|les|la|de|des|du)\s(\w+)\b#', $txt, $out) .
Astuce : PHP supporte aussi des expressions
rationnelles compatibles Perl, avec l'extension
PCRE functions. Ces fonctions supportent
des recherches non-gourmandes, des assertions ,
des sous -masques conditionnels et toute une gamme
de fonctionnalits absentes des expressions
rationnelles POSIX.
Le tableau de rsultat $out retournera cette fois ceci:
Matches [0]=>
[0]=> des expressions
[1]=> des recherches
[2]=> des assertions
[3]=> des sous
[4]=> de fonctionnalits
[5]=> des expressions
Capture [1]=>
[0]=> expressions
[1]=> recherches
[2]=> assertions
[3]=> sous
[4]=> fonctionnalits
[5]=> expressions
"So what", vous allez dire! Eh bien, le tableau de sortie n'est pas encombr
d'lments inutiles et il sera ainsi plus facile exploiter, de plus, les indices
des rfrences arrires (backreference) seront plus facile grer et, enfin, la
regex consommera moins de ressources et sera peu prs 10% plus vloce!
Une nano-seconde de gagne par-ci, une micro seconde par-l, muliplies
par le nombre d'accs votre script finiront bien par faire des secondes.
La mme remarque vaut pour une chane alternative du genre #PHP est un
(excellent)? langage de programmation# o le mot excellent est
facultatif. Si vous ne devez pas capturer ce mot, il vaut mieux crire #PHP
est un (?:excellent)? langage de programmation#.
Dmo
retour menu principal

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 8 sur 29

Les fonctions du type "callback"

Nous abordons ici une des fonctionnalits les plus utiles des fonctions PCRE.
Utiles car elles permettent d'utiliser, au sein d'une expression rgulire,
n'importe quelle fonction php ou perso.
Il y en a deux: preg_replace() avec l'option e ou bien preg_replace_callback
()
Callback veut simplement dire qu'on demande simplement PHP d'excuter
du code PHP ou une fonction au moment du remplacement. Imaginez, par
exemple, que vous souhaitiez mettre un mot sur deux en majuscule avec la
fonction php strtoupper() et le troisime mot l'envers avec la fonction
strrev() dans le texte suivant:
php supporte aussi des expressions rationnelles
compatibles Perl, avec l'extension PCRE functions.
Ces fonctions supportent des recherches nongourmandes, des assertions, des sous-masques
conditionnels et toute une gamme de
fonctionnalits absentes des expressions POSIX
Le code php suivant:
echo preg_replace('#(\w+)([^\w]*)(\w+)#e','strtoupper
("\1")."\2".strrev("\3")' , $txt);
retournera le texte ceci:
PHP etroppus AUSSI sed EXPRESSIONS sellennoitar
COMPATIBLES lreP, AVEC l'EXTENSION ERCP FUNCTIONS.
seC FONCTIONS tnetroppus DES sehcrehcer NONsednamruog, DES snoitressa, DES suos-MASQUES
slennoitidnoc ET etuot UNE emmag DE
stilannoitcnof ABSENTES sed EXPRESSIONS xisop
Vous percevez certainement la puissance de l'option e. Mais, dans le motif
de remplacement, il ne faut pas se tromper dans les simples et doubles
quotes sinon...
J'avoue que je dois chaque fois rflchir sur ce coup l. C'est ici que la
fonction preg_replace_callback() montre un de ses deux avantages. On
obtient exactement le mme rsultat avec ceci:
function maFonctionTordue($capture){
$txt = strtoupper($capture[1]).$capture
[2].strrev($capture[3]);
return $txt;

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 9 sur 29

}
echo preg_replace_callback('#(\w+)([^\w]*)(\w+)#',
"maFonctionTordue", $txt);
C'est plus clair il me semble. Plus de valses de guillemets! Ici, la fonction
callback "invoque" une fonction perso et lui passe implicitement les captures,
en argument, sous forme de tableau. Ce tableau est rcupr dans la
fonction en question et les diffrentes captures sont alors simplement
accessibles par leur indice.
L'autre avantage de preg_replace_callback() est qu'elle est souvent plus
rapide l'excution. Dans cet exemple, environ 3 fois plus rapide.
Son seul dsavantage mes yeux est qu'on ne peut passer d'autres
arguments que les captures. Si, pour les besoins de l'application, il est
ncessaire de passer d'autres arguments la fonction perso, on peut soit
dclarer ces arguments en GLOBAL au niveau de la fonction callback, soit il
faut en revenir preg_replace() avec l'option e mais cette fois de manire,
disons adapte...
Voici une illustration du premier cas. Les deux arguments passs la
fonction appele sont de simples balises <b> et </b>
function maFonctionTordue2($capture){
global $autreArgument1, $autreArgument2;
$txt = strtoupper($capture[1]).$capture
[2].$autreArgument1.strrev($capture
[3]).$autreArgument2;
return $txt;
}
$autreArgument1 = '<b>';
$autreArgument2 = '</b>';
echo preg_replace_callback('#(\w+)([^\w]*)(\w+)#',
"maFonctionTordue2", $txt);
Si, pour des raisons de cohsion de porte de variable, on rpugne utiliser
GLOBAL, il faut en revenir preg_replace() avec l'option e mais, cette fois,
adapte pour que la manipulation des captures soit simplifie:
function maFonctionTordue2($capture1, $capture2,
$capture3, $autreArgument1, $autreArgument2){
$txt = strtoupper
($capture1).$capture2.$autreArgument1.strrev
($capture3).$autreArgument2;
return $txt;
}

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 10 sur 29

$autreArgument1 = '<b>';
$autreArgument2 = '</b>';
echo preg_replace('#(\w+)([^\w]*)(\w+)
#e','maFonctionTordue2("\1","\2","\3",
"$autreArgument1", "$autreArgument2")' , $txt);
Les captures sont passes comme de simples arguments la fonction perso
afin d'y tre manipules plus simplement. Dans cette manire hybride, on
gagne en clart dans la manipulation des captures tout en ayant la
possibilit de passer des arguments supplmentaires la fonction invoque.
Dans les deux cas, ce code retournera:
PHP etroppus AUSSI sed EXPRESSIONS sellennoitar
COMPATIBLES lreP, AVEC l'EXTENSION ERCP FUNCTIONS.
seC FONCTIONS tnetroppus DES sehcrehcer NONsednamruog, DES snoitressa, DES suos-MASQUES
slennoitidnoc ET etuot UNE emmag DE
stilannoitcnof ABSENTES sed EXPRESSIONS xisop
Astuce: on peut pour galement passer des tableaux en argument comme
ceci:
function maFonctionTordue2($capture1, $capture2,
$capture3, $autreArgument){
$txt = strtoupper
($capture1).$capture2.$autreArgument[0].strrev
($capture3).$autreArgument[1];
return $txt;
}
$autreArgument = array('<b>','</b>');
echo preg_replace('#(\w+)([^\w]*)(\w+)
#e','maFonctionTordue2("\1","\2","\3",
$autreArgument)' , $txt);
retour menu principal

Les classes de caractres et types gnriques

Comme pour les POSIX, il est possible de dfinir des classes de caractres.
La notion de classe de caractres est bien connue. Quelques rappels:
Classe

Description

[a-p]

dfini tout alphabtique compris entre a et p.

[^a-p]

tout caractre qui n'est pas compris dans la fourchette a-p

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 11 sur 29

[a-p0-6] toutes les lettres de a p et chiffres de 0 6


Les mta classes POSIX [:classe:] sont acceptes au sein de classes PCRE.
Mais PCRE ajoute quelques classes supplmentaires appeles types
gnriques.
Types gnriques PCRE
\d

tout caractre dcimal

\D tout caractre qui n'est pas un caractre dcimal


\s

tout caractre blanc

\S tout caractre qui n'est pas un caractre blanc


\w tout caractre de "mot" [A-Za-z0-9_] plus les accentus
\W tout caractre qui n'est pas un caractre de "mot"
Les mta POSIX comme [:alpha:] ne sont acceptes que si elles sont
intgres dans une classe de caractres comme, par exemple, dans
preg_match('#[0-5[:alpha:]]#', $txt, $out) .
Situe en dehors d'une classe, le moteur regex renverra l'erreur
Compilation failed: POSIX named classes are supported only
within a class .
On pourrait remplacer cette classe POSIX par '#[0-5a-z]#' . Mais il y a une
diffrence: selon le setlocale(), la classe mta [:alpha:] comprendra les
accentus alors que [a-z] ne les comprend pas. On pourrait mettre le type
gnrique \w (qui comprend les accentus) comme dans: '#[0-5\w]#' mais
alors tous les numriques seront galement compris. Et le filtrage 0-5 ne
marchera pas. A mditer.
Un petit rappel des mta classes POSIX s'impose donc
Posix

Description

Pcre

Commentaire

[:digit:]

dcimal

\d

pas de diffrence

[:alnum:] tout caractre


alphanumrique

\w

idem \w sauf _

[:word:]

tout caractre de mot

\w

identique
[:alnum:] plus _

[:alpha:]

tout caractre
alphabtique

[a-zA-Z] plus
accentus

[:blank:]

espace et TAB

\s

\s comprend
galement le retour
ligne

[:punct:] tout caractre


imprimable sauf lettres
et chiffres

\W

idem [:punct:] plus


l'espace

[:print:]

[^\000-\037]

pas de diffrence

tout caractre
imprimable (octal
suprieur 037)

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 12 sur 29

[:space:] espace, H.tab, V.tab,


retour ligne

\s

[:upper:] tout alphabtique


majuscule

[A-Z]

[:lower:]

[a-z]

tout alphabtique
minuscule

\s ne comprend pas
le vertical tab

[a-z] ne comprend
pas les accentus]

Remarque importante: on a vu plus haut que les classes POSIX [:alpha:],


[:alnum:], [:word:] ou PCRE \w comprennent les accentus, mais seulement
si le bon jeu de caractres est install sur le serveur, sinon, un setlocale
(LC_CTYPE, 'fr_FR.ISO-8859-1') devrait rsoudre le problme. Vrifiez
sur votre serveur quelles sont les locales installes, elle peuvent prendre des
formes diffrentes selon votre systme: 'fr_FR', 'fra_fra' ...
Sur un systme Linux utilisez la commande $ locale -a pour les afficher.
En hbergement mutualis, renseignez-vous auprs de votre FAI.
Pour les mordus des specs, voir le dtail des diffrences dans les pages man
de la librairie C du moteur PCRE (mise jour par l'Universit de Cambridge).
Attention que l'implmentation de cette librairie dans PHP prsente parfois
de trs lgres diffrences.
retour menu principal

Les assertions PCRE

La bibliothque PCRE permet l'utilisation d'assertions simples qui prsentent


la particularit de ne pas consommer de caractres. Un peu comme les
ancrages ^ et $.
Assertions simples
\b limite de mot
\B pas limite de mot
\A dbut de la chane sujet (indpendant du mode multi-lignes)
\Z fin de la chane sujet ou nouvelle ligne la fin de la chane sujet
(indpendant du mode multi-lignes)
\z fin de la chane sujet (indpendant du mode multi-lignes)
Pour bien comprendre la particularit de non-consommation de caractres de
ce type d'assertion, voici un exemple dtaill. Imaginons le texte:
Voici simplement un simple exemple, trs simple,
mais pas simplet sur les assertions PCRE pourtant
pas si simples!
Je souhaite en extraire tous les mots simple ou simples . Pour viter
d'extraire le 'simple' de simplet ou de simplement , je dcide d'encadrer

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 13 sur 29

ce mot recherch avec des espaces comme par exemple dans ce motif:
preg_match_all('# simples? #', $txt, $out). La concordance (match),
contenue dans le tableau de rsultat $out[0] ne renverra qu'un seul 'simple',
le seul qui ne soit pas suivi de ponctuation:
Voici simplement un simple exemple, trs simple,
mais pas simplet sur les assertions PCRE pourtant
pas si simples!
Rat! Bon, on va alors rajouter la ponctuation dans une classe de caractres
preg_match_all('# simples?[ ,!.;:]#', $txt, $out) pour trouver une
concordance sur les simple ou simples suivis d'une ponctuation
ventuelle. Essayons:
Voici simplement un simple exemple, trs simple,
mais pas simplet sur les assertions PCRE pourtant
pas si simples!
Voil qui est mieux. Mais les concordances (matches) sont encadres soit
par des espaces, soit par de la ponctuation. Pas de problme, on va capturer
ce qui nous intresse avec des parenthses: preg_match_all('#
(simples?)[ ,!.;:]#', $txt, $out). Voici ce que a donne:
Voici simplement un simple exemple, trs simple ,
mais pas simplet sur les assertions PCRE pourtant
pas si simples !
On y est presque. Mais est-ce vraiment ncessaire de consommer de la
mmoire avec des parenthses capturantes (voir la discussion sur les
parenthses non capturantes)? C'est ici que l'utilisation de l'assertion simple
\b va nous tre utile
Essayons ceci: preg_match_all('#\bsimples?\b#', $txt, $out)
Voici simplement un simple exemple, trs simple,
mais pas simplet sur les assertions PCRE pourtant
pas si simples!
Et voil le travail. Cette dernire version est plus rapide que la version
capturante. Et dont le tableau de rsultat $out est plus facile utiliser
puisqu'il comporte deux fois moins d'lments. Pensez aux assertions
simples comme des indicateurs de position dans la chane cible. Cet indice de
position ne pointera pas sur un caractre mais bien entre deux caractres.
Voir ici une dmo de tout ceci. Essayez aussi l'option "timing" dans le
simulateur.
retour menu principal

Les options

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 14 sur 29

Il est possible de modifier le comportement par dfaut d'une fonction PCRE


grce aux options. Celles-ci se placent derrire le dlimiteur de fin de motif.
Voici les options les plus utiles:

i insensible la casse
s le point (dot) reconnat le retour ligne
m multilignes
e valuation de code php dans le groupe de remplacement
U ungreedy, pour rendre les quantificateurs non gourmands
x Pour commenter les motifs
autres A, D, S, X (moins courantes)

retour menu principal


i - Insensible la casse

C'est l'option la plus facile comprendre...et expliquer.


preg_replace('#les?#i', '***', $txt) remplacera toutes les
occurrences de 'le', 'les', 'LE' ou 'LES' par '***'. Tout simplement.
Voici l'occasion de prsenter une particularit de syntaxe souvent ignore. Il
est possible de d'activer ou de dsactiver, la demande, une option sur une
partie de regex. Un exemple vaut mieux qu'un long discours:
Dans la regex : preg_match_all('#concor(?-i)dance#i', $txt, $out),
l'option i est active pour l'ensemble du motif de manire classique derrire
le dlimiteur mais dsactive ponctuellement pour une partie du motif, grce
l'utilisation de l'interrupteur d'option (?-i). Voici le rsultat:
Cette regex doit matcher concordance et
CONCORdance, mais pas concorDANCE ou CONCORDAncE
Inversement, on peut bien sr activer et dsactiver une option la
demande. Le motif '#(?i)concor(?-i)dance#' aura exactement le mme
effet que le prcdent: l'insensibilit la casse est active pour la premire
parie du masque et dsactiv pour la deuxime.
Dmo
retour menu des options
s - Le dot comprend le retour ligne

Le dot remplace n'importe quel caractre. Mme s'il est souvent prfrable
d'utiliser des classes de caractres ngatives (voir plus loin), l'utilisation du
dot est fort rpandue. Mais seulement, le dot ne prendra pas le retour ligne.
Et un motif utilisant le dot risque de ne pas retourner le rsultat escompt.
Petit exemple pour capturer l'url de ceci: <a
href="http://url.com">lien</a>

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 15 sur 29

La regex la plus utilise pourrait ressembler ceci: preg_match('#<a


href="(.*?)">#', $txt, $out) Le rsultat est conforme aux attentes:
Petit exemple pour capturer l'url de ceci:
<a href=" http://url.com ">lien</a>
Mais imaginons qu'il y ait un retour ligne \r\n (windows) ou \n (linux) dans le
texte.
Petit exemple pour capturer l'url de ceci: <a
href="http://
url.com">lien</a>
Le parser html d'un navigateur n'y verra que du feu, mais pas la regex! Le
dot s'arrtera au retour ligne et n'ira pas plus loin. Elle ne trouvera donc ni le
" , ni le > pourtant ncessaires pour qu'il y aie concordance! Elle ne
trouvera aucune occurrence du motif et le tableau de rsultat $out restera
dsesprment vide!
Solution: rajouter l'option s derrire le dlimiteur preg_match('#<a
href="(.*?)">#s', $txt, $out) et l'affaire est dans le sac... et
l'occurrence dans le tableau $out[0]!
Mais, il y a un moyen plus simple et plus rapide de contourner le problme
du dot et du retour ligne. Simplement en remplaant le (.*?) par ([^"]*),
une classe de ngation de caractres qui prendra tout caractre (mme le
retour ligne) l'exception du " .
Dmo
retour menu des options
m - Multiligne

L'utilisation des ancrages ^ (dbut de chane) et $ (fin de chane) acclrent


considrablement le traitement d'une regex (voir partie astuce) mais quand
on a une chane qui comprend des retours lignes (comme dans un fichier plat
de donnes) l'ancrage de dbut ^ se trouvera au dbut de la premire ligne
de ce fichier et l'ancrage de fin $ aprs la toute dernire lettre de la dernire
ligne. Imaginons que je recherche l'adresse (rue et numro) de tous les
"HADDOCK" du fichier plat suivant (j'y ai rajout les ancrages afin d'illustrer
mon propos):
^000028;Mr;Chevalier;HADDOCK;rue de Rackam Le
Rouge;201;
000020;Mr;Marc;DUPONT;Avenue Roosens;253;
000021;Mme;Lucienne;MARTINO;rue Joseph Berger;25;
000022;Mr;Aucoin;DUBOIS;avenue des Gents;546;
000024;Mme;Marie;DUPOND;rue de Dinant;58;
000025;Mr;Reporter;TINTIN;clos des Sorbiers;111;
000026;Mr;Lechien;MILOU;rue du Chenil;215;

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 16 sur 29

000027;Mr;Typhon;TOURNESOL;boulevard
Transgnique;484;
000028;Mr;Archibald;HADDOCK;rue Whiskey;201;
000029;Mr;Marc;DUCHEMIN;clos de la Route;9;
000030;Mr;Philippe;DELAROUTE;chemin d'en
Haut;254;$
Je pourrais utiliser le motif '#^\d+;[^;]+;[^;]+;HADDOCK;([^;]+);([^;]
+);.*#'. L'ancrage ^ me permet d'crire ce motif de manire simple, en
dcomposant tous les champs d'une ligne de fichier, spars par un pointvirgule. En voici le rsultat:
000028;Mr;Chevalier;HADDOCK; rue de Rackam Le Rouge ; 201 ;
000020;Mr;Marc;DUPONT;Avenue Roosens;253;
000021;Mme;Lucienne;MARTINO;rue Joseph Berger;25;
000022;Mr;Aucoin;DUBOIS;avenue des Gents;546;
000024;Mme;Marie;DUPOND;rue de Dinant;58;
000025;Mr;Reporter;TINTIN;clos des Sorbiers;111;
000026;Mr;Lechien;MILOU;rue du Chenil;215;
000027;Mr;Typhon;TOURNESOL;boulevard
Transgnique;484;
000028;Mr;Archibald;HADDOCK;rue Whiskey;201;
000029;Mr;Marc;DUCHEMIN;clos de la Route;9;
000030;Mr;Philippe;DELAROUTE;chemin d'en Haut;254;
La premire ligne a bien t trouve, mais pas l'autre ligne, plus loin dans le
fichier! Tonnerre de Brest!
C'est ici que l'option m intervient.
L'option m (PCRE MULTILINE) demande au moteur regex de considrer
chaque retour ligne comme la fin de chane et le dbut de la suivante. Un
peu comme si ce moteur voyait le fichier plat comme ceci:
^000028;Mr;Chevalier;HADDOCK;rue de Rackam Le
Rouge;201;$
^000020;Mr;Marc;DUPONT;Avenue Roosens;253;$
^000021;Mme;Lucienne;MARTINO;rue Joseph
Berger;25;$
^000022;Mr;Aucoin;DUBOIS;avenue des Gents;546;$
De cette manire, avec l'option m, le motif '#^\d+;[^;]+;[^;]+;HADDOCK;
([^;]+);([^;]+);.*#m' donnera:
000028;Mr;Chevalier;HADDOCK; rue de Rackam Le Rouge ; 201 ;
000020;Mr;Marc;DUPONT;Avenue Roosens;253;

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 17 sur 29

000021;Mme;Lucienne;MARTINO;rue Joseph Berger;25;


000022;Mr;Aucoin;DUBOIS;avenue des Gents;546;
000024;Mme;Marie;DUPOND;rue de Dinant;58;
000025;Mr;Reporter;TINTIN;clos des Sorbiers;111;
000026;Mr;Lechien;MILOU;rue du Chenil;215;
000027;Mr;Typhon;TOURNESOL;boulevard
Transgnique;484;
000028;Mr;Archibald;HADDOCK; rue Whiskey ; 201 ;
000029;Mr;Marc;DUCHEMIN;clos de la Route;9;
000030;Mr;Philippe;DELAROUTE;chemin d'en Haut;254;
Et voil, toutes les lignes contenant le mot 'HADDOCK' sont trouves!
Ne vous privez pas d'utiliser les ancrages sur les fichiers plats (avec l'option
m s'il y a plusieurs lignes). Ils acclrent sensiblement le traitement. Le
mme motif sans l'ancrage ^ donne le rsultat escompt mais est plus de 4
fois plus lent sur cet exemple! Alors imaginez sur un fichier de plusieurs
milliers de lignes!
Dmo avec highlight des captures
Dmo avec timing comparatif
Note: On voit souvent cette option dans des motifs sans ancrage. C'est
inutile et sans effet. C'est comme mettre l'option s derrire un motif qui ne
comporte pas de dot. On ne met pas de parachute pour faire du pdalo!
retour menu des options
e - Evaluation de code PHP

Cette option a t aborde plus haut dans le cadre des fonctions du type
"callback" . Elle n'est utilise que pour la fonction preg_replace(), retour
menu des options
U - Option non gourmande

On a vu plus haut comment rendre un ou plusieurs quantificateur nongourmand dans une regex. L'option U permet de rendre tous les
quantificateurs d'une regex non gourmands. Ces deux motifs sont
parfaitement identiques :
'#<a href="(.*?)">(.*?)</a>#'
'#<a href="(.*)">(.*)</a>#U'
Maintenant, imaginons que vous ayez une loooongue srie de quantificateurs
et que tous doivent tre non-gourmands, sauf un, utilisez l'option globale U
pour l'ensemble du motif, et les "interrupteurs" d'options dcrits plus haut
pour dsactiver cette option sur une portion du motif.
retour menu des options
Option x

Le texte de la doc dit:

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 18 sur 29

"Avec cette option, les caractres d'espacement


sont ignors, sauf lorsqu'ils sont chapps, ou
l'intrieur d'une classe de caractres, et tous
les caractres entre # non chapps et en dehors
d'une classe de caractres, et le prochain
caractre de nouvelle ligne sont ignors. C'est
l'quivalent Perl de l'option /x : elle permet
l'ajout de commentaires dans les masques
compliqus."
Tout est dit. En gros, cette option introduite dans Perl 5 par Larry Wall, luimme inspir par une note de Jeffrey Friedl, permet d'crire des motifs
complexes de manire indente et, ventuellement, commente. Bref, a
permet d'crire des motifs de manire plus lisible. Sachant que les regex
sont plus faciles crire qu' (re)lire, ce n'est pas un luxe!
Ainsi, ce motif :
$motif = '/^\d+;[^;]+;[^;]+;HADDOCK;([^;]+);([^;]
+);.*/m';
preg_match_all($motif, $txt, $out);
peut s'crire de manire plus lisible:
$motif = "
/
# dlimiteur (ici je n' ai pas pris #,
rserv pour les commentaires)
^
# ancrage de dbut de chane
\d+;
# tout dcimal 1 fois ou plus, suivi
par ;
[^;]+;
# tout caractre sauf ; 1 fois ou
plus, suivi par ;
[^;]+;
# idem
HADDOCK; # les caractres HADDOCK suivi par ;
([^;]+); # capture tout caractre sauf ; 1
fois ou plus, suivi par ;
([^;]+); # idem
.*
# tout caractre sauf retour ligne 0
fois ou plus
/xm";
preg_match_all($motif, $txt, $out);
Attention! Les espaces du motif seront ignors. Si vous devez y mettre un
espace ou un # il FAUT les chapper sinon ils ne seront pas interprts!
retour menu des options
Autres options

Ces options sont moins utilises. Je reprends donc la doc in extenso:

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 19 sur 29

A (PCRE_ANCHORED) Avec cette option, le masque


est ancr de force, c'est--dire que le masque
doit s'appliquer juste au dbut de la chane sujet
pour tre considr comme trouv. Il est possible
de raliser le mme effet en ajoutant les mtacaractres adquats, ce qui est la seule manire
de le faire en Perl.
D (PCRE_DOLLAR_ENDONLY) Avec cette option, le
mta-caractre $ ne sera valable qu' la fin de la
chane sujet. Sans cette option, $ est aussi
valable avant une nouvelle ligne, si cette
dernire est le dernier caractre de la chane.
Cette option est ignore si l'option m est
active. Il n'y a pas d'quivalent en Perl.
S Lorsqu'un masque est utilis plusieurs fois,
cela vaut la peine de passer quelques instants de
plus pour l'analyser et optimiser le code pour
acclrer les traitements ultrieurs. Cette option
force cette analyse plus pousse. Actuellement,
cette analyse n'est utile que pour les masques non
ancrs, qui ne commencent pas par un caractre
fixe.
X (PCRE_EXTRA) Cette option ajoute d'autres
fonctionnalits incompatibles avec le PCRE de
Perl. Tous les anti-slash suivis d'une lettre qui
n'aurait pas de signification particulire cause
une erreur, permettant la rservation de ces
combinaisons pour des ajouts fonctionnels
ultrieurs. Par dfaut, comme en Perl, les antislash suivis d'une lettre sans signification
particulire sont traits comme des valeurs
littrales. Actuellement, cette option ne
dclenche pas d'autres fonctions.
u (PCRE8) Cette option dsactive les
fonctionnalits additionnelles de PCRE qui ne sont
pas compatibles avec Perl. Les chanes sont
traites comme des chanes UTF-8. Cette option est
disponible en PHP 4.1.0 et plus rcent sur plateforme Unix et en PHP 4.2.3 et plus rcent sur
plate-forme Windows.

retour menu des options


retour menu principal

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 20 sur 29

Les assertions

On a vu plus haut les assertions simples simples (telle que \b) qui ne
consomment pas de caractres. Vous vous rappelez qu'il convenait de les
voir comme un pointeur qui se trouverait entre deux caractres et pas sur un
caractre. Ils ne cherchent pas une concordance de caractre mais bien de
position. Et bien, les assertions font exactement la mme chose: elles se
placent entre deux caractres et testent les caractres suivants (lookahead)
ou prcdents (lookbehind). Il y en a 4:
Types d'assertion

Motif

Succs si le motif dans


l'assertion...

Les assertions arrires positives


(positive lookbehind)

(?
<=motif)

...trouve une concordance


gauche

Les assertions arrires ngatives


(negative lookbehind)

(?<!
motif)

...ne trouve pas de


concordance gauche

Les assertions avant positives


(positive lookahead)

(?=motif) ...trouve une concordance


droite

Les assertions avant ngatives


(negative lookahead)

(?!motif) ...ne trouve pas de


concordance droite

Bon, c'est trs bien, mais quoi a peut servir ces trucs l? Imaginons, une
fois encore, que l'on veuille extraire du texte suivant le dterminant de ,
mais uniquement s'il est suivi du mot caractre
L'implmentation des assertions arrires dplace
temporairement le pointeur de position vers
l'arrire, et cherche vrifier l'assertion. Si
le nombre de caractres est diffrent, la position
ne sera pas correcte, et l'assertion chouera. La
combinaison d'assertions arrires avec des sousmasques peut tre particulirement pratique fin
des chanes. Un exemple est donn la fin de
cette section.
Essayons ceci preg_match_all('#\bde\b(?=\scaractres)#', $txt,
$out);. Ce motif placera son pointeur juste aprs le e du mot de (mais
avant le caractre suivant, un espace) et vrifiera si ce qui suit est bien
caractre (avec un espace \s).
Rsultat:
L'implmentation des assertions arrires dplace
temporairement le pointeur de position vers
l'arrire, et cherche vrifier l'assertion. Si
le nombre de caractres est diffrent, la position

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 21 sur 29

ne sera pas correcte, et l'assertion chouera. La


combinaison d'assertions arrires avec des sousmasques peut tre particulirement pratique fin
des chanes. Un exemple est donn la fin de
cette section.
On pourrait objecter qu'on aurait pu obtenir la mme chose avec
preg_match_all('#\b(de)\b\scaractres#', $txt, $out) o le rsultat
sera captur dans $out[1][0]. Mme rsultat en effet mais, plus gourmand
(plus lent qu'avec le lookahead!)
Dmo
Mais imaginons maintenant que l'on cherche tous les de SAUF ceux qui sont
suivis par le mot caractres . L on ne s'en tirera pas sans une assertion,
ngative cette fois.
La fonction suivante php preg_match_all('#\bde\b(?!\scaractres)#',
$txt, $out) placera nouveau son pointeur aprs le e du mot de et
vrifiera, cette fois, si ce qui suit N'EST PAS la chane caractre (avec un
espace \s).
L'implmentation des assertions arrires dplace
temporairement le pointeur de position vers
l'arrire, et cherche vrifier l'assertion. Si
le nombre de caractres est diffrent, la position
ne sera pas correcte, et l'assertion chouera. La
combinaison d'assertions arrires avec des sousmasques peut tre particulirement pratique fin
des chanes. Un exemple est donn la fin de
cette section.
Je suppose que vous avez compris l'intrt des assertions. Les assertions
arrires fonctionnent exactement de la mme manire mais... dans l'autre
direction.
Cherchons dans le mme texte, par exemple, toutes les occurrences de
assertion ou assertions sauf celles prcdes de l' .
La fonction: preg_match_all("#(?<!l')assertions?#", $txt, $out)
devrait marcher. Essayez vous-mme dans le testeur pour en voir le
rsultat.
Dmo
retour menu principal

Les masques conditionnels

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 22 sur 29

On aborde ici une structure un peu plus complexe. Qui fait appel, entreautres, aux assertions (avant ou arrires, positives ou ngatives). De plus, la
doc PHP en franais est assez mal traduite.
Le prototype d'un sous-masque conditionnel (conditional sub-pattern) est le
suivant
(?(condition) masque_si_vrai | masque_sinon).
Il s'agit en fait d'une structure de contrle du type if-then-else. Lorsque nous
avons deux masques possibles, l'valuation de la condition dterminera
l'utilisation de l'un ou de l'autre.
La condition est soit une assertion soit un dcimal se rfrant une
rfrence arrire (capture).
Rien ne vaut un exemple. Nous avons une entte de courriel dans laquelle
on souhaite capturer les diffrents blocs to/from/subject/:
To: destinataire@example.com (commentaire inutile
ne pas capturer)
From: moi@example.net (commentaire encore plus
inutile)
Subject: Ces regex commencent srieusement me
gonfler!
La regex qui tombe sous le sens pourrait ressembler ceci (avec l'option x
pour commenter):
$txt="
To: destinataire@example.com (commentaire inutile)
From: me@example.net (commentaire encore plus
inutile)
Subject: Ces regex commencent srieusement me
gonfler!
";
$motif = '/^(From|To|Subject):\s(.*)/m';
// le mme motif avec commentaires (option x)
$motif='/
^(From|To|Subject)
# dbut de chane (ligne
puisque option m) suivi par From ou To ou Subject
:\s
# suivi par un : et un
espace
(.*)
# suivi par la capture de
tout caractre sauf retour ligne
# option m multiligne et x
pour les commentaires
/xm'
;
preg_match_all($motif, $txt, $out);

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 23 sur 29

Retournera:
To : destinataire@example.com (commentaire inutile)
From : me@example.net (commentaire encore plus inutile)
Subject : Ces regex commencent srieusement me gonfler!
Damned! Il a captur les "commentaires inutiles"! Il faudrait, idalement,
appliquer un masque spcifique pour capturer l'adresse email, et pas plus
loin. Par exemple un masque d'email comme "#\w+@\w+\.[a-z]+#"
capturera bien toute adresse email. Mais il ne capturera rien dans la ligne
Subject car il n'y a aucune adresse email. Comment faire?
Vous aurez devin que c'est ici que les sous-masques conditionnels
interviennent.

$motif = '/^((From|To)|Subject): ((?(2)\w+@\w+\.


[a-z]+|.+))/m';
// le mme motif, clat, avec commentaires
(option x)
$motif='/
^
# ancrage dbut de chane
(ligne puisque option m)
((From|To)|Subject):\s
# soit From ou To
(captur en \2) soit Subject suivi par : et espace
(
# paranthse capturante
(capture \3)
(?(2)
# if (From ou To) (2
est la rfrence arrire la capture plus haut)
\w+@\w+\.[a-z]+
# then toute chaine
alphanum suivie par @ suivie par aplphanum
# suivie par un point
et une chane alpha
|
# else
.+)
# tout caractre une
ou plusieurs fois
)
# fin capture \3 et
options m multiline et x pour commentaires
/xm'
;

preg_match_all($motif, $txt, $out);

Retournera
To : destinataire@example.com (commentaire
inutile)
From : me@example.net (commentaire encore plus

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 24 sur 29

inutile)
Subject : Ces regex commencent srieusement me gonfler!
Une extraction du tableau $out permettra de rassortir les couples To/From
-> adresse email et Subject -> texte sujet.
Dmo
retour menu principal

Les donnes binaires

Je n'ai jamais d l'utiliser ce jour mais la possibilit de comparer des


donnes binaires existe. On peut introduire des caractres binaires dans des
motifs de masques. Ces caractres peuvent s'introduire de deux manires:
Par octal: \ddd o d est un dcimal. (attention la confusion avec
les rfrences arrires \1 \2 etc...)
En hexa \xhh o hh est le code hexa
Pour plus de dtails, il y a une description dtaille sur les binaires dans la
section "Non-printing characters" dans les pages man.
retour menu principal

Astuces et optimisation

Une regex peut s'crire de nombreuses manires diffrentes. Certains motifs


seront beaucoup plus efficaces que d'autres. S'il s'agit de valider une courte
chane, pas de souci. La diffrence de vitesse d'excution ne se verra
presque pas. Quoique, une mico seconde par-ci, une nano seconde par-l...
(chanson connue!). Mais sur des fichiers volumineux, une regex mal crite
pourra mme mettre un serveur genoux!
Le moteur regex interne dispose de puissantes routines d'optimisation qui
vont d'abord analyser votre motif avant de se lancer dans le traitement
proprement dit, mais on peut toujours lui donner un srieux coup de main en
soignant ses motifs ds le dpart.
Comment faire pour les optimiser?

Utilisez le dot avec parcimonie


Supprimez les parenthses capturantes inutiles
Optimisez les alternatives.
Scindez vos regex trop complexes
Supprimez les options inutiles
Choisissez les fonctions callback appropries
Ancrez vos motifs
Testez et chronomtrez

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 25 sur 29

retour menu principal


Utilisez le dot avec parcimonie

On est souvent tent d'utiliser le dot qui prend tout caractre sauf le retour
ligne. Ce qui impose au moteur regex un nombre trs important de
combinaisons possibles (puisque le dot prend tout). On a vu, plus haut,
qu'utiliser une classe de ngation de caractres en lieu et place du dot tait
beaucoup plus efficace. Ne vous en privez pas. En plus, a vous vitera de
tomber dans le pige du dot qui s'arrte un ventuel retour ligne. Enfin, il
n'est pas ncessaire de rendre le quantificateur d'une telle classe nongourmand. Le quantificateur d'une classe de ngation de caractres
s'arrtera toujours au premier caractre ("ngativ") rencontr.
Par contre, si votre motif commence par un .* (trs mauvaise ide a,
surtout s'il n'y a pas d'ancrage ou de lettres fixes en dbut de motif!), le
moteur regex va pdaler dans la choucroute. Voyez:
On recherche une occurrence d'une chane se terminant par mot recherch
dans un texte de deux lignes:
premire ligne suivie par un retour ligne
et mot recherch
Si on cherche le mot cible avec le motif #(.*)mot recherch# impose au
moteur regex un grand nombre d'allers-retours dans la chane avant de
trouver une concordance. Il essayera d'abord toutes les combinaisons
possibles sur la premire ligne. Il ne trouvera rien. Ensuite seulement, il
attaquera la seconde ligne o il trouvera le motif recherch.
Si, maintenant on lui met l'option s #(.*)mot recherch#s, on l'aide en
diminuant sensiblement le nombre de backtracking. Il ira directement
jusqu' la fin de la deuxime ligne. Commencera son backtracking et
trouvera tout de suite le motif recherch.
Consquence: le deuxime motif est peu prs 45% plus rapide.
Dmo
Donc, attention au dot. Mal utilis il pourra planter votre regex!
retour menu astuces
Supprimez les parenthses capturantes inutiles

On a vu plus haut que si vous utilisez les parenthses pour dlimiter une
alternative, rendez-les non capturantes si vous n'avez pas besoin de les
capturer. Ainsi #(le|la|les|un|une)\s(\w+)# qui cherche capturer tous
les mots ((\w+)) sera 30% plus rapide si vous faites #
(?:le|la|les|un|une)\s(\w+)#.
Vous soulagerez le traitement de votre regex et simplifierez galement
l'utilisation des rfrences arrires et le contenu du tableau de rsultats.
retour menu astuces

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 26 sur 29

Optimisez vos alternatives

Tout d'abord, si possible, remplacez les alternatives par des classes. Il est
plus efficace d'crire [akpi] plutt que (?:a|k|p|i). De la mme manire il
vaut mieux faire (?:les?|des?|aux?) que (?:le|les|de|des|au|aux)
Enfin, sachez que lorsque le moteur regex aborde une alternative il prendra
les alternatives une par une en commenant par la premire. Ds qu'il
trouve une concordance, il s'arrtera l et n'analysera pas les autres
alternatives. Si vous commencez l'alternative par le choix le plus probable,
votre moteur regex s'arrtera plus tt. Exemple:
Pour acclrer le traitement de l'expression
rgulire, ordonnez vos alternatives !
Le motif non ordonn #
(?:le|la|les|de|du|des|au|aux|mes|tes|ses|nos|vos) alternatives#
sera plus lent que si vous placiez le choix le plus probable devant: #
(?:vos|la|les|de|du|des|au|aux|mes|tes|ses|nos) alternatives#
Dmo
retour menu astuces
Scindez vos regex trop complexes

Ne faites pas de regex trop compliques, avec de nombreux niveaux


d'alternatives. Il est souvent plus efficace de faire plusieurs petites regex
plutt qu'une longue. Songez au moteur regex qui doit analyser toutes les
combinaisons possibles d'occurrence d'un masque complexe. Et ce nombre
de combinaisons augmente de manire exponentielle avec la complexit du
motif.
Exemple: vous cherchez savoir si le nom d'un jour de semaine (Lundi,
Mardi etc...) se trouve dans un fichier. Vous serez tent de faire ceci:
// tous les noms de semaine avec un sparateur de
mot (assertion simple \b)
$mois = '#\b
(?:Lundi|Mardi|Mercredi|Jeudi|Vendredi|Samedi|Dimanche)
\b#';
if (preg_match($mois, $txt, $out)){
print_r($out);
}
C'est propre, c'est net. Mais est-ce efficace? On pourrait essayer de travailler
en boucle, en testant chaque nom de jour sparment et en quittant la
boucle ds qu'un de ceux-ci est trouv. Exactement le mme mcanisme
qu'une alternative regex. Essayons ceci:
// tableau avec un motif par jour de semaine
$mois = array('#\bLundi\b#', '#\bMardi\b#',
'#\bMercredi\b#', '#\bJeudi\b#',
'#\bVendredi\b#', '#\bSamedi\b#',

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 27 sur 29

'#\bDimanche\b#');
// boucle sur les jours de la semaine et teste si
ce jour se trouve dans le fichier
foreach ($mois as $v){
if (preg_match($v, $txt, $out)){
print_r($out);
break;
}
}
C'est un peu plus compliqu que de le faire en une seule passe, mais
tellement plus efficace! Prs de 10 fois plus rapide!
Et il y a encore certainement moyen d'amliorer la boucle avec une structure
de contrle while() plutt que la structure foreach() - break que j'ai
utilise pour clarifier l'exemple.
retour menu astuces
Supprimez les options inutiles

Il est inutile de demander au moteur de regex de chercher des possibilits


de concordances superflues. Comprenez bien l'utilit des options et
supprimez les si elles ne sont pas strictement ncessaires. Pas d'option s s'il
n'y a pas de dot dans le motif, ou d'option m s'il n'y a pas d'ancrage.
L'impact d'un "nettoyage" d'option sur la performance n'est pas norme mais
a aide parfois. Et puis ces options inutiles font un peu dsordre...
retour menu astuces
Choisissez la fonction callback approprie

Pour rappel, dans la plupart des cas, la fonction preg_replace_callback() sera


plus rapide que preg_replace() avec l'option e. A tester au cas par cas. Voir
la discussion sur ces fonctions.
retour menu astuces
Ancrez vos motifs

Si votre motif n'est pas ancr (^ ou $), ou s'il ne commence pas par un
caractre fixe, le moteur de regex va devoir stocker toute une srie d'tats
intermdiaires et devoir faire un srieux nombre de retours sur ces tats
(backtracking). Si vous fixez le dbut ou/et la fin d'une chane vous
diminuerez sensiblement ce backtracking inutile. Vous rappelez-vous
l'exemple du Capitaine Haddock plus haut et sa discussion sur l'utilit des
ancrages?
L'autre manire de faire est d'utiliser l'option S (majuscule) qui lancera une
optimisation du motif avant que le moteur ne commence la recherche
proprement dite. Faites l'essai de cette option dans le simulateur-testeur sur
le motif 2. Vous verrez qu'il fonctionnera peine plus rapidement avec cette
option. Le gain en vitesse d'excution beaucoup plus marqu si vous utilisez
les ancrages.
Dmo avec l'option S
Dmo avec ancrage

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 28 sur 29

retour menu astuces


Testez!

Aprs avoir utilis toutes les astuces ci-dessus, mettez-les l'preuve sur un
fichier grandeur nature et chronomtrez! Utilisez ce petit simulateur de
regex en mode timing. Ou bien faites-vous une petite fonction de
chronomtrage et excutez votre regex dans une boucle (quelques centaines
au maximum) pour lisser les rsultats.
$timeStart = microtime(true);
$nbLoops = 200;
while ($nbLoops--) {
/*
* code tester
*/
}
echo number_format(microtime
(true) - $timeStart, 4);
retour menu astuces
retour menu principal

Conclusion

Voil. Il y a encore tellement, tellement dire sur ce sujet... J'espre que ce


petit survol pourra contribuer une meilleure utilisation des regex dans vos
projets.
Les expressions rgulires PCRE sont peut-tre rbarbatives d'un premier
abord mais quelle puissance!
Alors, commencez sur de petites expressions simples et, lancez-vous! Elles
vous le rendront bien!

Liens et bibliographie

Testeur-simulateur de regex on line


expreg.com Autre site pour l'apprentissage des regex
Testeur gratuit tlcharger Regex Coach
Historique des expressions rationnelles Wikipedia
Mastering Regular Expressions Jeffrey Friedl isbn 0-596-00289-0
retour menu principal

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Tutoriel Expressions rgulires PCRE

Page 29 sur 29

Tuto et testeur par Jean-Luc Lacroix (regains scs) qui accueillera vos commentaires et/ou critiques avec
plaisir. Surtout si vous avez de bonnes ides pour illustrer l'une ou l'autre fonctionnalit sympa des
PCRE. Merci Nicolas Chambrier pour sa relecture et ses conseils. Contact:

http://lumadis.be/regex/tuto_pcre.php

14/12/2014

Vous aimerez peut-être aussi