| 
Les Expressions Régulières![[Illustration]](../../common/images/illustration30.gif)  Résumé: 
Les expressions régulières sont utilisées dans les
recherches et modifications de texte sophistiquées. On les rencontre
dans de nombreux éditeurs, dans les programmes d'extraction et dans
certains langages. 
 
 
 
IntroductionLes expressions régulières se rencontrent dans de nombreux
éditeurs sophistiqués comme vi et emacs, dans les programmes
grep/egrep, et dans les langages tels awk, perl et sed.Les expressions régulières sont utilisées pour
les recherches et les modifications de texte contextuelles avancées.
L'expression régulière est une description formelle d'une
forme à comparer à une chaîne de caractères. 
 La première fois que j'ai vu quelqu'un utiliser les expressions
régulières, j'ai été fasciné. Des éditions
de texte et des recherches qui auraient pris normalement plusieurs heures
pouvaient être exécutées en quelques secondes. Cependant,
je ne comprenais pas un mot lorsque je voyais les expressions à
l'écran. Elles n'étaient qu'une étrange combinaison
de points, barres, étoiles et autres caractères. J'étais
néanmoins déterminé à percer le mystère,
et j'ai rapidement découvert que les expressions régulières
sont très faciles à utiliser. Elles suivent quelques simples
règles de syntaxe. 
 Bien que les expressions régulières soient très
répandues dans le monde Unix, il n'existe pas de "langage standard
pour les expressions régulières". Il existe plutôt
quelques dialectes différents. Par exemple, il existe deux programmes
de recherche de chaînes dans les fichiers: grep et egrep. Les deux
utilisent les expressions réguilières, mais avec des possibilités
légèrement différentes. Perl dispose probablement
du plus grand ensemble d'expressions régulières. Heureusement,
les principes sont toujours les mêmes. Une fois comprise l'idée
de base, il est aisé d'apprendre les détails des différents
dialectes. 
 Cet article vous présentera les principes de base, et vous pourrez
vous référer au manuel des différents programmes pour
connaître en détail ses règles et possibilités
particulières.  
 
Un exemple simpleSupposons que vous ayez l'agenda d'une société sous la forme
suivante:Phone Name  ID
     ...
     ...
3412    Bob 123
3834  Jonny 333
1248   Kate 634
1423   Tony 567
2567  Peter 435
3567  Alice 535
1548  Kerry 534
     ...La société comporte 500 personnes. Les données sont
conservées dans un simple fichier texte en ASCII. Supposons que
le premier chiffre du numéro de téléphone corresponde
au bâtiment où le collaborateur travaille. Comment savoir
qui travaille dans le bâtiment 1? Les expressions régulières permettent de trouver rapidement
la réponse: 
 grep '^1' phonelist.txt
or
egrep '^1' phonelist.txt
or
perl -ne 'print if (/^1/)' phonelist.txtEn d'autres termes, cela signifie rechercher toutes les lignes qui commencent
par un "1". le symbôle "^" peut être traduit par "début
de ligne". Il force l'expression entière à correspondre seulement
si "1" est le premier caractère de la ligne. 
La syntaxe
Formes à caractère uniqueLa pierre angulaire d'une expression régulière est la forme
à caractère unique. Elle ne correspond qu'à ce caractère.
Dans l'exemple précédent, le "1" est une forme à caractère
unique. Il ne correspond qu'à "1" dans le texte.
 Un autre exemple de formes à caractère unique est: 
 egrep 'Kerry' phonelist.txtCette forme est composée de plusieurs formes à caractère
unique (les lettres K,e,r,r,y). Les caractères peuvent être regroupés dans un ensemble.
L'ensemble est constitué d'une liste de caractères placée
entre crochets. L'ensemble lui-même est une forme à caractère
unique, car il coïncide à un unique caractère du texte.
Il suffit qu'un des caractères de l'ensemble soit présent
dans le texte pour que la correspondance soit vérifiée. Par
exemple: 
[abc]    est une forme à caractère unique
qui correspond
 indifféremment
à l'un des caractères a, b ou c.
 [ab0-9]  est une forme à caractère unique qui correspond
 aux caractères
a ou b ou un caractère entre 0 et 9.
 [a-zA-Z0-9\-] cet ensemble correspond à un caractère
unique qui
 est soit une lettre minuscule ou majuscule,
 soit un chiffre, ou encore le signe moins.
 Essayons: 
 egrep '^1[348]' phonelist.txtCette recherche extrait les lignes du fichier qui commencent par 13, 14
ou 18. Nous venons de voir que pour la plupart, les caractères ASCII
ne correspondent qu'à eux-mêmes. Néanmois, certains
ont une signification spéciale. Les crochets définissent
un ensemble, le signe moins "-" détermine une plage. Pour annuler
le caractère spécial d'un symbôle, il suffit de le
faire précéder d'une barre inversée "\". C'est ce
que nous avions dans l'exemple précédent. Dans certains dialectes,
certaines séquences de contrôle commencent par une barre inversée,
qu'il faut alors supprimer pour retrouver la signification normale. 
 Le point est un caractère spécial important. Il correspond
à n'importe quel caractère sauf le saut de ligne. Par exemple: 
 grep '^.2' phonelist.txt
 or
egrep '^.2' phonelist.txtCes commandes donnent toutes les lignes dont le deuxième caractère
est "2", le premier caractère étant quelconque. Les ensembles peuvent être inversés (négation) en
indiquant "[^" à la place de "[" comme marque de début d'ensemble.
Ainsi, le symbôle "^" ne signifie plus début de ligne, mais
la combinaison "[^" indique ensemble inversé. 
 [0-9]    est une forme à caractère unique
qui correspond
 aux chiffres entre
zéro et neuf.
 [^0-9]   correspond à tout caractère ne représentant
pas un chiffre.
 [^abc]   correspond à tout caractère différent
de "a", de "b", et de "c".
 .       le point correspond à
n'importe quel caractère sauf le saut de ligne,
 il est donc équivalent
à "[^\n]" où "\n" est le saut de ligne.
 Pour trouver toutes les lignes ne commencant pas par "1", il faudrait
écrire: 
 grep '^[^1]' phonelist.txt
ou
egrep '^[^1]' phonelist.txt 
AncragesNous avons vu que le symbôle "^" correspond à un début
de ligne. Les ancrages sont des expressions régulières spécialement
prévues pour correspondre à une position dans le texte et
non à un caractère du texte.^  correspond à un début de ligne
$  correspond à une fin de lignePour retrouver les personnes ayant le matricule 567 dans votre agenda,
vous pouvez utiliser: egrep '567$' phonelist.txtCette commande donnera les lignes se terminant par 567. 
RépétitionsIl est possible de spécifier combien de fois une forme à
caractère unique doit se répéter.
Remarque: les différentes variations de vi ont leur option magique
positionnée pour fonctionner comme indiqué.
| Description | grep | egrep | perl | vi | vim | vile | elvis | emacs |  
| Zéro ou plusieurs fois | * | * | * | * | * | * | * | * |  
| Une ou plusieurs fois | \{1,\} | + | + |  | \+ | \+ | \+ | + |  
| Zéro ou une fois | \? | ? | ? |  | \= | \? | \= | ? |  
| Entre n et  m fois | \{n,m\} |  | {n,m} |  |  |  | \{n,m\} | \{n,m\} |  Un exemple sur l'agenda téléphonique: 
 ....
1248   Kate 634
....
1548  Kerry 534
....Pour retrouver une ligne qui commence par "1", puis au moins un chiffre,
puis au moins un espace, enfin un nom commencant par "K", on pourrait écrire: grep '^1[0-9]\{1,\} \{1,\}K' phonelist.txt ou utiliser "*" et répéter l'ensemble "[0-9]" et l'espace:grep '^1[0-9][0-9]*  *K' phonelist.txtou egrep '^1[0-9]+ +K' phonelist.txtou perl -ne 'print if (/^1[0-9]+ +K/)' phonelist.txtNotez que la répétition intervient sur la forme à
caractère unique précédente. Ainsi, "23*4" ne signifie
PAS "2 puis 3 puis n'importe quoi puis 4" (cela s'écrirait "23.*4"),
mais signifie "une fois 2, peut-être plusieurs fois 3, puis une fois
4". Il faut aussi remarquer que les répétitions sont gourmandes.
C'est à dire que la première répétition dans
la forme s'étend le plus possible vers la droite du texte. 
 Ainsi l'expression "^1.*4" renverrait la ligne suivante en entier: 
 1548  Kerry 534depuis le premier caractère jusqu'au dernier. Ca n'a pas d'incidence
pour les outils comme grep, mais la différence est importante pour
les éditions et les substitutions. 
Mémorisation avec les parenthèsesLa contruction de mémorisation avec les parenthèses ne change
pas la correspondance d'une expression mais permet de mémoriser
le texte renvoyé, afin d'être utilisé plus loin dans
l'expression.Le texte mémorisé est disponible à travers des
variables. La première parenthèse de l'expression est la
variable une, la seconde parenthèse la variable deux, etc. 
 
Par exemple, l'expression "[a-z][a-z]" renverrait deux lettres minuscules.
On peut réutiliser les deux caractères renvoyés afin
de rechercher des formes comme 'otto':
| Nom du programme | Syntaxe des parenthèses | Syntaxe des variables |  
| grep | \(\) | \1 |  
| egrep | () | \1 |  
| perl | () | \1 or ${1} |  
| vi,vim,vile,elvis | \(\) | \1 |  
| emacs | \(\) | \1 |  egrep '([a-z])([a-z])\2\1'La variable 1 contient le caractère "o", la variable 2 le caractère
"t". Cette expression retrouverait aussi le texte "anna", mais pas "toto". Cette construction de mémorisation avec les parenthèses
n'est pas souvent utilisée pour des recherches de texte comme otto
ou anna, mais plutôt lors des éditions ou pour des substitutions.  
 
Edition de texte avec les expressions régulièresPour l'édition, on peut utiliser un éditeur comme vi ou emacs,
ou utiliser des outils ou langages comme perl.Dans emacs, la commande à exécuter est  query-replace-regexp
(demande-remplacement-expressions régulières M-x), qui peut
aussi être affectée à une touche de fonction. Vous
pouvez aussi utiliser la commande replace-regexp (remplacement-expressions 
régulières). La première commande est interactive,
la seconde ne l'est pas. 
 Dans vi, la commande de substitution est "%s/ / /gc". Le symbôle
"%" fait référence à l'étendue d'action de
la commande (fichier complet), et peut être remplacé par n'importe
quelle étendue appropriée. Par exemple, dans vim utilisez
shift-v, marquez une étendue puis exécutez la substitution
sur cette étendue. Je ne m'étendrai pas plus sur vim, qui
mériterait un article à lui seul. Dans la commande de substitution,
"gc" fait référence à la version interactive. La version
sans confirmation est "s/ / /g". 
 Interactif signifie ici demande de confirmation avant chaque remplacement. 
 Avec perl vous pouvez utiliser: 
 perl -pe 's/ / /g' Essayons quelques exemples. Supposons que le plan de numérotation
soit modifié, et qu'on doive insérer un "2" après
le deuxième caractère pour tous les numéros commencant
par "1". Par exemple, le numéro 1423 devient 14223. 
 Le répertoire téléphonique avant modification: 
 Phone Name  ID
     ...
3412    Bob 123
3834  Jonny 333
1248   Kate 634
1423   Tony 567
2567  Peter 435
3567  Alice 535
1548  Kerry 534
     ...En exécutant l'une des commandes suivantes: vi:    s/^\(1.\)/\12/g
emacs: ^\(1.\)   replaced by  \12
perl:  perl -pe 's/^(1.)/${1}2/g' phonelist.txtle répertoire devient:Phone Name  ID
     ...
3412    Bob 123
3834  Jonny 333
12248   Kate 634
14223   Tony 567
2567  Peter 435
3567  Alice 535
15248  Kerry 534
     ...Perl n'est pas limité aux variables mémoires " \1" à
"\9". Ainsi, "\12" ferait référence à la douzième
variable mémoire, d'où la syntaxe "${1}" pour référencer
la première variable mémoire. Vous remarquerez que l'alignement dans la liste est dorénavant
perturbé. Comment remédier à ce problème? On
peut tester si le caractère en cinquième position est un
espace, et sinon ajouter un espace: 
 vi:     s/^\(....\) /\1  /g
emacs:  '^\(....\) '  replaced by  '\1  '
perl:   perl -pe 's/^(....) /${1}  /g' phonelist.txtMaintenant le répertoire a retrouvé un aspect plus agréable:Phone Name  ID
      ...
3412     Bob 123
3834   Jonny 333
12248   Kate 634
14223   Tony 567
2567   Peter 435
3567   Alice 535
15248  Kerry 534
      ...Un collègue a manuellement édité le répertoire
en introduisant accidentellement des espaces au début de certaines
lignes. Comment les enlever? Phone Name  ID
      ...
3412     Bob 123
     3834   Jonny 333
12248   Kate 634
14223   Tony 567
 2567   Peter 435
3567   Alice 535
  15248  Kerry 534
      ...Ces commandes devraient apporter la solution: vi:     s/^  *//  (il y a 2 espaces car nous n'avons pas le +)
emacs:  '^ +'  remplacé par la chaîne vide 
perl:   perl -pe 's/^ +//' phonelist.txtAutre exemple pour les développeurs. Vous écrivez un programme
qui comporte les deux variables "temp" et "temporaire". Maintenant vous
souhaiteriez remplacer le nom de variable  "temp" par "compteur".
Si la chaîne "temp" est simplement remplacée par "compteur",
alors l'autre variable prendra le nom "compteuroraire", ce qui n'est pas
ce que vous souhaitez! Les expressions régulières peuvent le faire. Remplacez
simplement "temp([^o])" par "compteur\1". C'est à dire que l'on
recherche maintenant la chaîne "temp" puis n'importe quel caractère
différent de "o". (Une solution alternative aurait été
d'utiliser les limites, mais nous n'avons pas discuté ce type d'ancrage.) 
 J'espère que cet article aura suscité votre intérêt
pour les expressions régulières. Je vous conseille de jeter
un oeil aux pages d'aide et à la documentation de votre éditeur
favori pour plus de détail. 
 Bien sûr cet article n'est qu'une brève introduction, il
existe d'autres caractères spéciaux, comme par exemple l'altération
(sorte de OU logique), et aussi les limites de mots évoquées
précédemment. 
 Bonne édition, amusez-vous bien! 
 Traduit par Jean-Denis Girard. |