Sommaire Carte Index Rechercher Nouvelles Archives Liens LF
[Top Bar]
[Bottom Bar]
[Photo of the Author] 
Guido Socher  
A propos de l'auteur: adore Linux parce que c'est un système gratuit et aussi car travailler avec d'autres membres de la communauté Linux tout autour du monde est très satisfaisant. Il passe son temps libre avec sa copine, écoute les programmes de BBC World Service, se promène en vélo à travers la campagne, et aime jouer avec Linux. 

Table des matières
Introduction 
Un exemple simple 
La syntaxe 
Edition de texte avec les expressions régulières
 

Les Expressions Régulières

[Illustration] 

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. 


Introduction

Les 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 simple

Supposons 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.txt
En 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 unique

La 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.txt
Cette 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.txt
Cette 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.txt
Ces 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

Ancrages

Nous 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 ligne
Pour retrouver les personnes ayant le matricule 567 dans votre agenda, vous pouvez utiliser: 
egrep '567$' phonelist.txt
Cette commande donnera les lignes se terminant par 567.

Répétitions

Il est possible de spécifier combien de fois une forme à caractère unique doit se répéter. 
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\}
Remarque: les différentes variations de vi ont leur option magique positionnée pour fonctionner comme indiqué. 

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.txt
ou 
egrep '^1[0-9]+ +K' phonelist.txt
ou 
perl -ne 'print if (/^1[0-9]+ +K/)' phonelist.txt
Notez 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 534
depuis 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èses

La 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. 
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
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': 

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ères

Pour 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.txt
le 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.txt
Maintenant 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.txt
Autre 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.

Ce site est maintenu par Miguel Angel Sepulveda 
© Guido Socher 1998 
LinuxFocus 1998