Les expressions régulières.

Définition.

Une expression régulière est constituée d'un modèle (un gabarit) que l'on va tenter d'assortir à une chaine.
La comparaison, couronnée ou non de succés, pourra conduire à prendre une décision ou à accomplir une action.
Afin de présenter quelques exemples sous leur forme la plus simple, nous allons utiliser le fichier suivant (entree.txt).

c:\progs> type entree.txtent
Ligne 1 contenant la chaine aaa
Ligne 2 contenant la chaine bbb
Ligne 3 contenant la chaine aaabbb
Ligne 4 contenant la chaine bbaaabb
Ligne 5 contenant la chaine aaaaaa
Ligne 6 contenant la chaine ababab
Ligne 7 contenant la chaine bbaabb
Ligne 8 contenant la chaine aaaa
Ligne 9 contenant la chaine aa
c:\progs>
  

Les modèles que nous tenterons de mettre en évidence seront des suites de a et de b.

Un premier exemple.

Un exemple simple valant mieux qu'un long discours, nous allons en commenter un.

Programme reg1.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV = ("Entree.txt");
while (<>) {
  if (/aaa/) {
    print "$_";
  }
}
    
c:\progs> perl reg1.plent
Ligne 1 contenant la chaine aaa
Ligne 3 contenant la chaine aaabbb
Ligne 4 contenant la chaine bbaaabb
Ligne 5 contenant la chaine aaaaaa
Ligne 8 contenant la chaine aaaa
c:\progs>
    
@ARGV = ("Entree.txt"); Déclaration du fichier "entrée.txt en tant que source de l'opérateur diamand.
while (<>) { Accés aux lignes successives du fichier et test de fin de fichier. Aucun scalaire n'étant spécifié en tant de destination, les lignes seront stockées dans la variable scalaire standard $_.
if (/aaa/) { La condition se présente entre deux slash (/) indiquant qu'il s'agit d'une expression régulière. Il n'y a aucune indication à propos du scalaire qui contient la chaîne de caractères sur laquelle doit porter le recherche, ce sera donc par définition la variable scalaire standard $_. Le modèle que l'on recherche est une suite de trois fois le caractère "a". Si ce modèle est trouvé, alors la réponse sera 'vrai', dans le cas contraire, elle sera 'faux'.
print "$_"; Si la réponse est 'vrai', c'est à dire que le modèle recherché est présent dans la chaîne de référence, on imprime la ligne en question.
} Sinon on se contente de passer à la ligne suivante.
} Jusqu'à épuisement des lignes du fichier d'entrée.

Le facteur multiplicatif (*).

Une astérisque (*) située immédiatement derrière un caractère permet d'indiquer que ce caractère peut se présenter de 0 à n fois.

ab*a
  

Est un modèle qui reonnaitra toutes les cibles de type.

aa Zéro "b" entre deux "a"
aba Un "b" entre deux "a"
abba Deux "b" entre deux "a"
ab....ba n "b" entre deux "a"
Programme reg2.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV = ("Entree");
while (<>) {
  if (/ab*b/) {
    print "$_";
  }
}
    
c:\progs> perl reg2.plent
igne 3 contenant la chaine aaabbb
Ligne 4 contenant la chaine bbaaabb
Ligne 6 contenant la chaine ababab
Ligne 7 contenant la chaine bbaabb
c:\progs>
    

La recherche porte sur un modèle comportant un "a" suivi de 0 à n fois un "b" suivi de un b. en résumé, un "a" suivi de au moins un "b".

Modification de la cible.

Une fois le modèle trouvé, il est possible de procéder à une modification de la cible.

Programme reg3.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV = ("Entree");
while (<>) {
  if (s/ab*b/XXXX/) {
    print "$_";
  }
}
    
c:\progs> perl reg3.plent
Ligne 3 contenant la chaine aaXXXX
Ligne 4 contenant la chaine bbaaXXXX
Ligne 6 contenant la chaine XXXXabab
Ligne 7 contenant la chaine bbaXXXX
c:\progs>
    

Le modèle que l'on recherche est le même que celui vu à l'exercice précédent. Mais ici on ne se contente pas de le mettre en évidence. On lui substitue une autre chaîne, dans l'exemple, la chaîne "xxxx".
L'écriture de l'expression régulière fait apparaître trois champs.
s pour substitute, indique que l'on procèdera à une substitution.
/ab*b/ le modèle recherché.
/xxxx/ les caractères qui seront substitués au modèle une fois ce dernier détecté.

Les jokers et les ensembles.

Le caractère point (.) s'assortira à n'importe quel caractère, exeption faite du \n (fin de ligne)

/a.b/
  

Recherche tout groupe de trois caractères dont le premier est un "a", et le troisième un "b" sans plus de précisions concernant le troisième, sinon que ce n'est surement pas un \n.

Il est possible de spécifier ensemble de caractère afin de rechercher un caractère qui en fait partie. Cet ensemble sera représenté par la liste de ses composantes encadreé de crochets.

/b[aeiou]b/
  

Recherche un des elements de l'ensemble (une voyelle) comprise entre deux "b"

Il est aussi possible de complémenter l'ensemble. Le caractère "^" présent au debut de l'ensemble indique que l'on souhaite rechercher un caractère ne faisant pas partie de l'ensemble en question.

/b[^aeiou]b/
  

Recherche deux "b" encadrant un caractère qui peut être n'importe quoi sauf une voyelle.

Les ensembles de caractères consécutifs peuvent se présenter sous une forme raccourcie, laissant au systéme le soin de le développer.

[0-9] L'ensemble des caractères compris entre 0 et 9.
[a-z] L'ensemble des lettres minuscules.
[A-Z] L'ensemble des lettres majuscules.

Les classes prédéfinies.

Les ensembles que nous venons de voir sont d'une grande utilité, ils nous permettent de définir des classes de caractères spécifiques.

[0-9] Les chiffres.
[^0-9] Tout sauf un chiffre.
[a_zA-Z0-9_] L'ensemble des caractères utilisables pour représenter des identificateurs.
[^a_zA-Z0-9_] Tout caractère sauf ceux utilisé pour représenter des identificateurs.

Afin de simplifier l'écriture des expressions régulières, certaines de ces classes sont prédéfinies.

Nom de la classe Signification Classe équivalente.
\d Chiffres [0-9]
\D Tout sauf un chiffre [^0-9]
\w Caractères faisant partie de la construction des mots [a_zA-Z0-9_]
\W Tout sauf les caractères faisant partie de la construction des mots (les séparateurs) [a_zA-Z0-9_]
\s Les séparateurs? [\t\n\f\r]
\S Tout sauf un séparateur [^\t\n\f\r]

Exemple :

/\W\d*\dA\d\W/  

Est le modèle qui va permettre de chercher.

\W Un caractère ne faisant pas partie de ceux composant les mots (séparateur ou signe de ponctuation.
\d* Suivi d'un nombre de chiffres compris entre 0 et n.
\d Suivi d'un chiffre. En résumé, au moins un chiffre.
A Suivi du caractère "A" (en majuscule).
\d Suivi d'un chiffre.
\W Et se terminant par un caractère ne faisant pas partie de ceux composant les mots (séparateur ou signe de ponctuation.

Voici quelques cibles répondant à ce modèle.

,12A3.
-1A1;
(123A4)
  

Et en voici ne répondant pas à ce modèle.

12A3.
-A1;
(123AA4)  

Les autres facteurs multiplicatifs.

Nous avons évoqué l'astérisque, facteur multiplicatif 0 à n.
Il existe deux autres facteurs prédéfinis.
Le + représentant le facteur multiplicatif de 1 à n.
Le ? représentant le facteur multiplicatif par 0 ou 1.

Là ne s'arrètent pas les possibilités, l'usager a toute lattitude pour definir explicitement ses propres facteurs multiplicatifs.

Programme reg4.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV = ("Entree");
while (<>) {
  if (/xy{2,3}/) {
    print "$_";
  }
}
    
c:\progs> perl reg4.plent
Ligne 3 contenant la chaine xxxyyy
Ligne 4 contenant la chaine yyxxxyy
Ligne 7 contenant la chaine yyxxyy
c:\progs>
    

On cherche la chaine de caractères constituée du caractère "x" suivi d'un nombre de "y" compris entre 2 et 3.

Programme reg5.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV = ("Entree");
while (<>) {
  if (/xy{3,}/) {
    print "$_";
  }
}
    
c:\progs> perl reg5.plent
Ligne 3 contenant la chaine xxxyyy
c:\progs>
    

On cherche la chaine de caractères constituée du caractère "x" suivi d'un nombre de "y" supérieur ou égal à 3.

Programme reg6.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV = ("Entree");
while (<>) {
  if (/xy{0,3}/) {
    print "$_";
  }
}
    
c:\progs> perl reg6.plent
Ligne 1 contenant la chaine xxx
Ligne 3 contenant la chaine xxxyyy
Ligne 4 contenant la chaine yyxxxyy
Ligne 5 contenant la chaine xxxxxx
Ligne 6 contenant la chaine xyxyxy
Ligne 7 contenant la chaine yyxxyy
Ligne 8 contenant la chaine xxxx
Ligne 9 contenant la chaine xx 
c:\progs>
    

On cherche la chaine de caractères constituée du caractère "x" suivi d'un nombre de "y" inférieur ou égal à 3.

Exemple.

Validation du nom d'une variable.

Un nom de scalaire commence par un $, immediatement suivi d' une lettre (majuscule ou minuscule) et se continue par un nombre quelconque de lettres majuscules, lettres minuscules, blancs soulignés ou chiffres.

Un nom de liste commence par un @, immediatement suivi d' une lettre (majuscule ou minuscule) et se continue par un nombre quelconque de lettres majuscules, lettres minuscules, blancs soulignés ou chiffres.

Un nom de hash commence par un %, immediatement suivi d' une lettre (majuscule ou minuscule) et se continue par un nombre quelconque de lettres majuscules, lettres minuscules, blancs soulignés ou chiffres.

Un nom de descripteur de fichier commence par une lettre (majuscule ou minuscule) et se continue par un nombre quelconque de lettres majuscules, lettres minuscules, blancs soulignés ou chiffres.

Ecrire un programme Perl qui lise un nom de variable et le valide en indiquant quelle forme il représente.

c:\progs> type valid.plent 
#!usr/bin/perl
print "Donner un nom de variable : ";
chop ();
if (/\$[A-Za-z][_0-9A-Za-z]*/){
  print ("Le nom $_ représente une variable scalaire.\n";
} elsif (/@[A-Za-z][_0-9A-Za-z]*/){
   print ("Le nom $_ représente une liste.\n";
} elsif (/%[A-Za-z][_0-9A-Za-z]*/){
   print ("Le nom $_ représente un hash.\n";
} elsif (/[A-Za-z][_0-9A-Za-z]*/){
   print ("Le nom $_ représente un fichier.\n";
} else {
  print ("Le nom $_ ne représente par une variable.\n");
}
c:\progs>
  

Mémoriser des modèles.

Les parenthèses permettent de mémoriser une chaine de caractères qui pourra, de cette manière, être référencée ultérieurement.

Programme reg7.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV=("Entree");
while (<>) {
  if (/x(.)y\1/) {
    print "$_";
  }
}
    
c:\progs> perl reg7.plent
Ligne 3 contenant la chaine xxxyyy
c:\progs>
    

Dans l'exemple ci dessus, la référence \1 designe la premiere (et dans ce cas, la seule) forme mémorisée dans l'expression régulière.

On recherche le caractère "x" suivi d'un caractère que l'on ne comnnait pas, mais que l'on mémorise, lui même suivi d'un caractère "y" et se terminant par un caractère identique à celui qui se trouvait en seconde position et auquel on fait référence par son nom (\1).

Ainsi par exemple, /a(.)b(.)c\2d\1/ permet de déterminer une forme qui serait composée de la lettre "a" suivi d'un caractère (c1) suivi de b suivi d'un caractère (c2) suivi de c suivi du caractère c2 (le second mémorisé) auquel on fait référence par son nom (\2), suivi de d suivi du caractère c1 (le premier mémorisé) et auquel on fait référence par son nom (\1).

La chaine "axbycydx' est donc une cible reconnue.

Une mémoire peut contenir un modèle composé de plusiaurs caractères couvrant le modèle situé entre parenthèses. Ainsi, le modèle

/a(.*)b\1/
  

Appliqué à la chaine

"axxxbxxx"
  

Mémorisera les caractères compris entre "a" et "b", c'est à dire "xxx". C'est cette chaîne qui sera confrontée avec celle qui suit le caractère "b" pour décider si oui ou non il y a coincidence. Dans ces conditions.

Pour la cible La réponse sera
axxxbxxx/td> vrai
ayybyy vrai
axxxbxx faux
axybxy vrai
ab vrai

Le modèle mémorisé peut se retrouver indifférement dans la partie modèle ou dans la partie substitution.

Ainsi, considérons la chaîne

"AA-aaa-BB-bbb-C-C-ccc-DD"
  

Et soumettons la à l'expression régulière.

s/A(.*)B(.*)C/X\1Y\2Z/
  

On obtiendra le résultat.
"XA-aaa-BY-bbb-C-Z-ccc-DD"
  

Explications

s/A(.*)B(.*)C/X\1Y\2Z/ On cherche la lettre "A"
s/A(.*)B(.*)C/X\1Y\2Z/ On mémorise alors la chaine qui suit le "A"
s/A(.*)B(.*)C/X\1Y\2Z/ Et ce, jusqu'à l'ocurence du caractère "B". La première mémoire \1 contient "A-aaa-B"
s/A(.*)B(.*)C/X\1Y\2Z/ On mémorise alors la chaine qui suit le "B"
s/A(.*)B(.*)C/X\1Y\2Z/ Et ce, jusqu'à l'ocurence du caractère "C". La seconde mémoire \2 contient "-bbb-C-"
s/A(.*)B(.*)C/X\1Y\2Z/ On passe alors à la substition.
s/A(.*)B(.*)C/X\1Y\2Z/ On commence par écrire la lettre "X"
s/A(.*)B(.*)C/X\1Y\2Z/ Suivie de la chaîne contenue dans la première mémoire("A-aaa-B"). L'état de la chaîne à ce point est "XA-aaa-B".
s/A(.*)B(.*)C/X\1Y\2Z/ On écrit alors le caractère "Y". La chaîne est devenue "XA-aaa-BY"
s/A(.*)B(.*)C/X\1Y\2Z/ On fait alors suivre la chaîne du contenu de la seconde mémoire, elle devient "XA-aaa-BY-bbb-C-"
s/A(.*)B(.*)C/X\1Y\2Z/ Et on termine l'expression régulière en écrivant un "Z" pour obtenir "XA-aaa-BY-bbb-C-Z"
s/A(.*)B(.*)C/X\1Y\2Z/ Reste la fin de la chaîne (tout ce qui suit le second "C" et qui n'était pas concerné. Elle ne bouge pas et reste donc à sa place."XA-aaa-BY-bbb-C-Z-ccc-DD"

La troisième ligne mérite une explication supplémentaire. Il est en effet nécessaire d'expliquer pourquoi le modèle

A(.*)B
  

Ciblé sur la chaîne

"A-aaa-BB"
  

Mémorise

"A-aaa-B"
  

Et non pas

"A-aaa-"
  

Ceci provient du fait que la couverture du modèle est dite "avide".

La couverture sera toujours tirée afin de couvrir la cible maximale.
Dans le cas qui nous interesse, les "B" successifs seront intégrés et seul le dernier mettra fin à la couverture.

Il est important de toujours garder ce principe à l'esprit lorsqu'on travaille sur les expressions régulières. <:h3>

Considérons la chaîne.

"Yxxxxxxxx"
  

A laquelle est appliqué le modèle

/Y(x*)/
  

Le principe d'avidité conduit, en couvrant toujours la cible au maximum, à mémoriser la chaîne

"xxxxxxxx"
  

Les ancrages.

Il s'agit de la possibilité qui est offerte d'ancrer un modèle à un emplacement particulier de la cible considérée.
Début de mot ou de chaîne.
Milieu de mot ou de chaîne.
Fin de mot ou de chaîne.

Programme reg8.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV=("Entree");
while (<>) {
  if (/xyy\b/) {
    print "$_";
  }
}
    
c:\progs> perl reg8.plent
Ligne 4 contenant la chaine yyxxxyy
Ligne 7 contenant la chaine yyxxyy
c:\progs>
    

On cherche une chaine constituée des lettrex xyy ancrées en fin de mot, c'est à dire immédiatement suivies d'un séparateur (\b).

Programme reg9.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV=("Entree");
while (<>) {
  if (/\bxxx/) {
    print "$_";
  }
}
    
c:\progs> perl reg9.plent
Ligne 1 contenant la chaine xxx
Ligne 3 contenant la chaine xxxyyy
Ligne 5 contenant la chaine xxxxxx
Ligne 8 contenant la chaine xxxx
c:\progs>
    

On cherche une chaine constituée des lettrex xyy ancrées en début de mot, c'est à dire immédiatement précédées d'un séparateur (\b).

Programme reg10.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV=("Entree");
while (<>) {
  if (/\bxxx\b/) {
    print "$_";
  }
}
    
c:\progs> perl reg10.plent
Ligne 1 contenant la chaine xxx
c:\progs>
    

On cherche une chaine constituée des lettrex xyy ancrées en début et en fin de mot, c'est à dire immédiatement précédées et immédiatement suivies d'un séparateur (\b). Soit le mot xxx.

Nous avons vu, au moment ou il a été question des classes prédéfinies qu'une classe était représentée en lettre minuscule alors que la classe complémentaire était représentée en lettre majuscule. La classe complémentaire de \b sera donc \B. Elle représentera tout ce qui n'est pas un séparateur.

Programme reg11.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV=("Entree");
while (<>) {
  if (/\bxy\B/) {
    print "$_";
  }
}
    
c:\progs> perl reg11.plent
Ligne 6 contenant la chaine xyxyxy
c:\progs>
    

On cherche une chaine constituée des lettres xy ancrées en début de mot, c'est à dire immédiatement précédées d'un séparateur et ne constituant pas un mot à elles toute seules .

Programme reg12.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV=("Entree");
while (<>) {
  if (/\Bxy\B/) {
    print "$_";
  }
}
    
c:\progs> perl reg12.plent
Ligne 3 contenant la chaine xxxyyy
Ligne 4 contenant la chaine yyxxxyy
Ligne 6 contenant la chaine xyxyxy
Ligne 7 contenant la chaine yyxxyy
c:\progs>
    

On cherche une chaine constituée des lettrex xy ancrées en milieu de mot, c'est à dire ne constituant pas un mot à elles toute seules.

Il est aussi possible d'ancrer les caractères par rapport à la chaîne cible en utilisant les caractères spéciaux ^et $. Il faut toutefois faire attention a leur emplacement dans l'expression régulière afin que leur interprétation ne soit pas erronée.
Le caractère "^", si il est le premier caractère de l'expression régulière, permet d'ancrer le modèle au début de la cible.
Le caractère "$", si il est le dernier caractère de l'expression régulière, permet d'ancrer le modèle à la fin de la cible.

Exemple Commentaire
/^a/ "vrai" si la cible commence par le caractère "a".
/\^a/ "vrai" si la cible contient la chaîne "^a"
/a^/ "vrai" si la cible contient la chaîne "a^".
/a$/ "vrai" si la cible se termine par le caractère "a".
/a\$/ "vrai" si la cible contient la chaîne "a$".
/$a/ "vrai" si la cible contient la chaîne "$a".

$a permet de sélectionner la chaine "$a"Dans une expression régulière, la priorité des opérateurs est la suivante.

Priorité Opérateur Représentation
Maximale Parenthèses ( )
. Multiplicateurs + * ? (m,n)
. Séquences, ancrages abc ^ $ \b \B
Minimale choix |

Ainsi, /a|b*/ ne signifie pas n fois le caractère "a" ou n fois le caractère "b". L'opérateur multiplicatif ayant une priorité supérieure à l'opérateur de choix, il sera appliqué en premier. La cible que l'on recherche sera donc un caractère "a" suivi de n caractères "b" (abbb..b).

Pour repérer la chaîne n fois le caractère "a" ou n fois le caractère "b", l'expression régulière aurait été /(a|b)*/.

Exemple Commentaire
/abc*/ "a" suivi de "b" suivi de zéro ou plusieurs "c".
/(abc)* / 0 ou n fois la chaîne "abc".
/^x|y/ Un "x" en début de mot suivi de tout caractère, ou un "y" où qu'il soit dans la chaine.
/a|bc|d/ Toute chaine contenant le caractère "a" ou les caractères "bc" ou le caractère "d".
/(a|b)(c|d)/ Toute chaine contenant le caractère "a" ou le caractère "b" suivis du caractère "c" ou du caractère "d" ("ac", "ad", "b"c, "bd").

Les cibles.

Jusqu'à présent, nous avons considéré que la cible à laquelle on soumet le modèle se trouvait dans la variable d'entrée standard ($_). Dans ces conditions, il n'était pas nécessaire de le préciser. Si ce n'est pas le cas, c'est à dire si la cible se trouve dans une variable scalaire quelconque, l'opérateur =~ va nous permettre de soumettre l'expression régulière qui se trouve à sa droite à la variable scalaire spécifiée à sa gauche.
Le résultat de cette opération est "vrai" si une concordance est trouvée, "faux" sinon.

Programme reg13.pl Exécution sur l'écran
#!/usr/bin/perl;
$cible = "Bonjour";
print ("Recherche de la chaine Bo.\n");
print ("Resultat : ",$cible =~ /^Bo/);
print "\n";
print ("Recherche de la chaine ja\n");
print ("Resultat : ",$cible =~ /ja/);
    
c:\progs> perl reg13.plent
Recherche de la chaine Bo.
Resultat : 1
Recherche de la chaine ja.
Resultat : 
c:\progs>
    

C'est ainsi que l'on peut écrire.

if($cible =~ /expr. régulière/) {
  Séquence pour condition vraie;
} else {
  Séquence pour condition fausse;
}
  

A noter aussi que la partie gauche de l'opérateur =~ peut être <STDIN>.

Programme reg14.pl Exécution sur l'écran
#!/usr/bin/perl;
print ("Quelle est votre reponse ? ");
if ( =~/^[oO]/) {
  print ("La reponse est oui.\n\n");
}
else {
  print ("La reponse est non.\n\n");
}
    
c:\progs> perl reg14.plent
Quelle est votre reponse ? Ouient
La reponse est oui.
c:\progs> perl reg14.plent
Quelle est votre reponse ? Nonent
La reponse est non.
c:\progs>
    

Voici à titre d'exemple quelques exercices amusants auxquels tout amateur de mots croisés peut se trouver confronté.

Nous avons récupéré un dictionnaire du français que nous avons stocké dans un fichier référencé "francais.txt".

Afin d'en faciliter l'utilisation, il ne contient aucun caractères non ascii sinon le trait d'union.

a-cotea-cotes
a-coup
a-coups
a-peu-pres
a-pic
a-propos
a-valoir
a-venir
abaissa
.....
.....
irreformables
irrefrenable
irrefrenables
irrefutable
irrefutables
irrefute
irrefutee
irrefutees
....
.....
zoologiste
zoologistes
zoom
zooms
zoos
zouave
zouaves
zozoter
zyeuter

Livrons nous maintenant à quelques recherches.

Trouver tous les mots qui commencent par les lettres 'shu'.

Programme dico1.pl Exécution sur l'écran
#!usr/bin/perl
@ARGV =("francais.txt");
while  ($ligne = <>) {
  chop ($ligne);
  if ($ligne =~/^shu/) {
    print "$ligne\n";
  }
}
    
c:\progs> perl dico1.plent
shunter
c:\progs>
    

Trouver tous les mots qui finissent par les lettres 'ley'.

Programme dico2.pl Exécution sur l'écran
#!usr/bin/perl
@ARGV =("francais.txt");
while  ($ligne = <>) {
  chop ($ligne);
  if ($ligne =~/ley$/) {
    print "$ligne\n";
  }
}
    
c:\progs> perl dico2.plent
trolley
volley
c:\progs>
    

Trouver tous les mots qui contiennent la chaine 'aer' suivie de deux caractères quelconques suivi d'un 'y'

Programme dico3.pl Exécution sur l'écran
#!usr/bin/perl
@ARGV =("francais.txt");
while  ($ligne = <>) {
  chop ($ligne);
  if ($ligne =~/aer..y/) {
    print "$ligne\n";
  }
}
    
c:\progs> perl dico3.plent
aerodynamique
c:\progs>
    

Trouver tous les mots qui contiennent l'une des trois lettres g,j ou p suivie des deux caractères 'er' puis de deux caractères quelconques puis d'un 'y'.

Programme dico4.pl Exécution sur l'écran
#!usr/bin/perl
@ARGV =("francais.txt");
while  ($ligne = <>) {
  chop ($ligne);
  if ($ligne =~/[gjp]er..y/) {
    print "$ligne\n";
  }
}
    
c:\progs> perl dico4.plent
jersey
peroxyder
c:\progs>
    

Trouver tous les mots qui commencent par une lettre de l'intervalle a-d (a,b,c ou d) puis qui contienne les deux lettres 'hry' puis, soit une lettre pris dans l'intervalle a-i, soit la lettre "s".

Programme dico5.pl Exécution sur l'écran
#!usr/bin/perl
@ARGV =("francais.txt");
while  ($ligne = <>) {
  chop ($ligne);
  if ($ligne =~/^[a-d]hry[a-is]/) { 
    print "$ligne\n";
  }
}
    
c:\progs> perl dico5.plent
chrysalide
chrysantheme
c:\progs>
    

Trouver tous les mots qui commencent par la lettre k suivie de au moins deux voyelles.

Programme dico6.pl Exécution sur l'écran
#!usr/bin/perl
@ARGV =("francais.txt");
while  ($ligne = <>) {
  chop ($ligne);
  if ($ligne =~/^k[aeiou][aeiou][aeiou]*/){ 
    print "$ligne\n";
  }
}
    
c:\progs> perl dico6.plent
kaolin
kiosque 
c:\progs>
    

Trouver tous les mots qui commencent par quo et se terminent par t.

Programme dico7.pl Exécution sur l'écran
#!usr/bin/perl
@ARGV =("francais.txt");
while  ($ligne = <>) {
    chop ($ligne);
  if ($ligne =~/^quo[a-z]*t$/) {
  print "$ligne\n";
  }
}
    
c:\progs> perl dico7.plent
quolibet
quotidiennement
quotient 
c:\progs>
    

Autre possibilité.

Programme dico8.pl Exécution sur l'écran
#!usr/bin/perl
@ARGV =("francais.txt");
while  ($ligne = <>) {
      chop ($ligne);
     if ($ligne =~/^quo.*t$/) {
     print "$ligne\n";
     }
}    
c:\progs> perl dico8.plent
quolibet
quote-part
quotidiennement
quotient 
c:\progs>
    

Et pour finir, résoudre la vieille devinette qui consiste à trouver tous les mots contenant les voyelles "a", "e", "i", "o", "u" dans cet ordre, les lettres n'étant pas forcément adjacentes.

Programme dico9.pl Exécution sur l'écran
#!/usr/bin/perl;
#!usr/bin/perl
@ARGV =("francais.txt");
while  ($ligne = <>) {
  chop ($ligne);
  if ($ligne =~/[a-z]*a[a-z]*e[a-z]*i[a-z]*o[a-z]*u/) {
    print "$ligne\n";
  }
}
    
c:\progs> perl dico9.plent
bacteriologique
c:\progs>
    

Ou bien, autre solution un peu plus générale.

Programme dico10.pl Exécution sur l'écran
#!usr/bin/perl
@ARGV =("francais.txt");
while  ($ligne = <>) {
  chop ($ligne);
  if ($ligne =~/.*a.*e.*i.*o.*u/) {
    print "$ligne\n";
  }
}
    
c:\progs> perl dico10.plent
bacteriologique
garde-chiourme
c:\progs>
    

Majuscules, minuscules.

Reprenons le programme 14 qui testait une réponse.

Programme reg14.pl Exécution sur l'écran
#!/usr/bin/perl;
print ("Quelle est votre reponse ? ");
if ( =~/^[oO]/) {
  print ("La reponse est oui.\n\n");
}
else {
  print ("La reponse est non.\n\n");
}
    
c:\progs> perl reg14.plent
Quelle est votre reponse ? Ouient
La reponse est oui.
c:\progs> perl reg14.plent
Quelle est votre reponse ? Nonent
La reponse est non.
c:\progs>
    

La réponse attendue pouvant être "Oui" ou "oui", la methode simple consistait à créer un ensemble compose d'un o (minuscule) et d'un O (majuscule) et de tester le premier caractère de la reponse. C'est ce qui est fait dans l'exemple.
L'option "i" (pour ignore case) va nous permettre d'ignorer, sur l'ensemble de la cible, le fait que les caractères soient représentés en majuscule ou en minuscule.
Les options apparaissent en fin d'expression, aprés le dernier slash.
Ainsi, le programme 14 pourrait être réécrit sous la forme.

Programme reg14bis.pl Exécution sur l'écran
#!/usr/bin/perl;
print ("Quelle est votre reponse ? ");
if ( =~/^o/i) {
  print ("La reponse est oui.\n\n");
}
else {
  print ("La reponse est non.\n\n");
}
    
c:\progs> perl reg14bis.plent
Quelle est votre reponse ? Ouilent
La reponse est oui.
c:\progs> perl reg14bis.pllent
Quelle est votre reponse ? Nonlent
La reponse est non.
c:\progs>
    

Changement de délimiteur.

Il est possible que le caractère "/" doive être présent dans le modèle à rechercher. Il existe une possibilité, c'est de représenter le caractère en question au moyen de la chaîne "\/".

Exemple, soit à reconnaitre le modèle "/etc/passwd" dans une cible lue au clavier.

Programme reg15.pl Exécution sur l'écran
#!/usr/bin/perl;
$path = ;
if ($path =~ /^\/etc\/passwd/) {
  print ("Chaine reconnue");
} else {
  print ("Chaine inconnue");
}
    
c:\progs> perl reg15.plent
/etc/passwdent
Chaine reconnue
c:\progs>
    

Une autre solution consiste à changer le délimiteur identifiant l'expression régulière afinn que ce dernier ne soit plus le "/". Ce changement de délimiteur est spécifié par la lettre "m" (pour modify).

Programme reg16.pl Exécution sur l'écran
#!/usr/bin/perl;
$path = ;
if ($path =~ m*^/etc/passwd*) {
# le delimiteur est maintenant * 
     print ("Chaine reconnue");
     }
   else {
     print ("Chaine inconnue");
}
    
c:\progs> perl reg16.plent
/etc/passwdent
Chaine reconnue

c:\progs>
    
Programme reg16bis.pl Exécution sur l'écran
#!/usr/bin/perl;
$path = ;
if ($path =~ m&^/etc/passwd&) {
# le delimiteur est maintenant &
     print ("Chaine reconnue");
     }
   else {
     print ("Chaine inconnue");
}
    
c:\progs> perl reg16bis.plent
/etc/passwdent
Chaine reconnue
c:\progs>
    

Les variables.

Afin de simplifier l'écriture des expressions régulières, le modèle peut être mémorisé dans une variabe.

Programme reg17.pl Exécution sur l'écran
#!/usr/bin/perl;
$mot = "jour";
$cible = "Bonjour a tous.";
if ($cible =~ /$mot/) {
  print ("le nom : $nom\n");
  print ("contient la chaine : $mot\n");
} else {
  print ("Rien");
}
    
c:\progs> perl reg17.plent
la cible: Bonjour a tous.
contient la chaine : jour
c:\progs>
    

On peut compliquer les choses.

Programme reg18.pl Exécution sur l'écran
#!/usr/bin/perl;
$cible = "Bonjour a tous.";
print "Quel mot rechercher ?\n");
$mot = ;
chop ($mot);
if ($cible =~ /$mot/) {
  print ("La cible : $nom\n");
  print ("contient : $mot\n");
} else {
else {
     print ("La cible : $mot ");
     print ("ne contient pas :$nom\n");

}
    
c:\progs> perl reg18.plent
Quel mot rechercher ?
bonjourent
La cible: Bonjour a tous.
ne contient pas : bonjour
c:\progs> perl reg18.plent
Quel mot rechercher ?
[bB]onjourent
La cible: Bonjour a tous.
contient : [bB]onjour
c:\progs>
    

Les variables liées aux expressions régulières.

Nous avons déjà évoqué la possibilité de mémoriser des éléments de la cible qui couvrent certains modèles afin de les réutiliser dans le corps de l'expression régulière.
Ces variables qui sont référencées \1, \2, ..., \n dans le corps de l'expression sont aussi disponibles dans le corps de programme. Elles s'appellent respectivement $1, $2, ..., $n.
Elles sont positionnées chaque fois qu'une mémorisation est demandée dans le corps d'une expression régulière.
Reprenons l'exemple de la cible

"AA-aaa-BB-bbb-C-C-ccc-DD"
  

A laquelle est soumise l'expression

s/A(.*)B(.*)C/X\1Y\2Z/
  
Programme reg19.pl Exécution sur l'écran
#!/usr/bin/perl;
$cible = "AA-aaa-BB-bbb-C-C-ccc-DD";
$cible =~ s/A(.*)B(.*)C/X\1Y\2Z/;
print ("Valeur de $1 : $1\n");
print ("Valeur de $2 : $2\n");
print ("Valeur finale de la cible :\n");
print ($cible"\n");
    
c:\progs> perl reg19.plent
Valeur de $1 : AA-aaa-B
Valeur de $2 : -bbb-C-
Valeur finale de la cible :
XA-aaa-BY-bbb-C-Z-ccc-DD
c:\progs>
    

Un moyen intéressant d'utiliser cette particularité pourrait être.

Programme reg20.pl Exécution sur l'écran
#!/usr/bin/perl;
chomp();
($m1,$m2) = /(\w+)\W+(\w+)/;
print ("Premier mot : $m1\n");
print ("Second mot : $m2\n");
    
c:\progs> perl eg20.plent
Bonjour Monsieur
Premier mot : Bonjour
Second mot : Monsieur
c:\progs>
    

Il existe par ailleurs trois variables spécifiques prédéfinies.

Nom Contenu.
$& Chaine qui couvre le modèle.
$` Partie qui précède le modèle.
$' Partie qui suit le modèle
Programme reg21.pl Exécution sur l'écran
#!/usr/bin/perl;
$cible = "AA-aaa-BB-bbb-C-C-ccc-DD";
$cible =~ /(b+)/;
print ("Chaine reperee : $&\n");
print ("Chaine qui precede : $`\n");
print ("Chaine qui suit : $'\n");
    
c:\progs> perl reg21.plent
Chaine reperee : bbb
Chaine qui precede : AA-aaa-BB-
Chaine qui suit : -C-C-ccc-DD
c:\progs>
    

Ces variables étant positionnées chaque fois qu'une expression régulière est utilisée, il est nécessaire, si on désire les utiliser plusieurs fois au cours du programme, de les mémoriser dans des variables déclarées.

Les options.

Options Action
g Pour general. Change toutes les occurences de la forme sélectionnée
i Pour ignore case. Ignore le fait que la forme sélectionnée est en majuscule ou en minuscule.
e Pour evaluate. Permet l'évaluation de la chaine de remplacement comme une expression.
m Pour multiple lines. La chaine de test occupe plusieurs lignes.
o Pour one. L'évaluation n'a lieu qu'une fois.
s Pour single line. Le traitement n'a lieu que sur une ligne.
x Pour extended. Permet de présenter une expression régulière sur plusieurs lignes.
Programme reg22.pl Exécution sur l'écran
#!/usr/bin/perl;
$cible = "AA-aaa-BB-bbb-C-C-ccc-DD";
print ("Avant : $cible\n");
$cible =~ s/C/xx/;
print ("Apres : $cible\n");
    
c:\progs> perl reg22.plent
Avant : AA-aaa-BB-bbb-C-C-ccc-DD
Apres : AA-aaa-BB-bbb-xx-C-ccc-DD
c:\progs>
    

On substitue à la première occurence de "C" la chaîne "xx'.

Programme reg23.pl Exécution sur l'écran
#!/usr/bin/perl;
$cible = "AA-aaa-BB-bbb-C-C-ccc-DD";
print ("Avant : $cible\n");
$cible =~ s/C/xx/g;
print ("Apres : $cible\n");
    
c:\progs> perl reg23.plent
Avant : AA-aaa-BB-bbb-C-C-ccc-DD
Apres : AA-aaa-BB-bbb-xx-xx-ccc-DD
c:\progs>
    

L'option g (pour general) permet de forcer la substitution à toutes les occurence de "C".

Programme reg24.pl Exécution sur l'écran
#!/usr/bin/perl;
$cible = "AA-aaa-BB-bbb-C-C-ccc-DD";
print ("Avant : $cible\n");
$cible =~ s/C/xx/gi;
print ("Apres : $cible\n");
    
c:\progs> perl reg24.plent
Avant : AA-aaa-BB-bbb-C-C-ccc-DD
Apres : AA-aaa-BB-bbb-xx-xx-xxxxxx-DD
c:\progs>
    

L'option g (pour general) et i (pour ignore case) permet de forcer la substitution à toutes les occurence de "C"qu'il se présente en majuscules ou en minuscules.

Programme reg25.pl Exécution sur l'écran
#!/usr/bin/perl;
$cible = "AA-aaa-BB-bbb-C-C-ccc-DD";
print ("Avant : $cible\n");
$cible =~  s/(\w+)(\W+)/<$1>/g;
print "$cible\n";
    
c:\progs> perl reg25.plent
Avant : AA-aaa-BB-bbb-C-C-ccc-DD
Apres : <AA><aaa><BB><bbb><C><C><ccc><DD>
c:\progs>
    

On mémorise chaque mot et son séparateur (\w+)(\W+), on replace le mot mémorisé entre <> <$1> et on ne tient pas compte du séparateur. Cette opération est effectuée pour l'ensemble des éléments de la cible (option g).

Voici un problème clasique. On dispose d'un texte mal formaté. Pour le moment, nous ne considèrerons qu'il ne contient que des mots séparés par des espaces ou des tabulations. Il est stocké dans le fichier "texte.txt.

                Ce texte   contient des         tas             
 d'espaces              et     de  tabulations          
 mal            placees.                
   le but    est de              le reformater     
        afin                 qu'il    soit presentable
  
Programme reg26.pl Exécution sur l'écran
#!usr/bin/perl
@ARGV =("ttab.txt");
print "Texte reformate : \n\n");
while  ($ligne =<>) {
  $ligne =~s/^[ \t]+//;
  $ligne =~s/[ \t]+$//;
  $ligne =~s/[ \t]+/ /g;
  chomp($ligne);
print "-$ligne-\n";
    
c:\progs> perl reg26.plent
Texte reformate :

-Ce texte contient des tas-
-d'espaces et de tabulations-
-mal placées.-
-le but est de le reformater-
-afin qu'il soit présentable.-
c:\progs>
    

On utilise successivement trois expressions régulières.
La première va supprimer les espaces et les tabulations en début de ligne.
La seconde va supprimer les espaces et les tabulations en fin de ligne.
La troisième va supprimer les espaces et les tabulations en les remplaçant par un unique espace.

L'évaluation d'une expression.

L'option e (pour evaluate) va permettre, lors d'une substition, de procéder à un calcul sur les variables qui auront été préalablement mémorisées.
Par exemple, la cible contient une série de lettres suivis d'un chiffre. On desire dupliquer la chaîne de lettres autant de fois qu'indiqué par le chiffre qui suit.
La chaîne

Ab2x4y6
  

Doit donner après traitement. /h3>
AbAbxxxxyyyyyy
  
Programme reg27.pl Exécution sur l'écran
#!usr/bin/perl
$chaine = "Ab2x4y6";
print ("Chaine origine :\n");
print ("$chaine\n"=;
$chaine =~s/([a-zA-Z]+)(\d+)/$1 x $2/ge;
print ("Chaine apres traitement :\n");
print "$chaine\n";    
c:\progs> perl reg27.plent
Chaine origine :
Ab2x4y6 
Chaine apres traitement :
AbAbxxxxyyyyyy
c:\progs>
    

Le champ de substitution contient une opération de multiplication de chaînes. on reproduit la chaîne contenue dans la variable $1 autant de fois que la valeur contenue dans la variable $2.

Autre exemple, on dispose d'un texte qui contient des caractères alphabétiques et des nombres

Le nombre entier un : 1.
Le nombre entier deux : 2.
Le nombre entier quatre : 4.
Le nombre entier huit : 8.
Et pourquoi pas le nombre entier cent : 100.
Cette egalité est vraie : 250 x 3 = 750.
Et on finit en donnant la date :
nous sommes le 22 Avril 2000.
  

On souhaite multiplier tous les nombres de ce texte par 2.

Programme reg28.pl Exécution sur l'écran
#!/usr/bin/perl;
#!usr/bin/perl
@ARGV =("Txtnb.txt");
while  ($ligne = <>) {
     $ligne =~s/\d+/$& * 2/eg;
     print "$ligne";
}
    
c:\progs> perl reg28.plent
Le nombre entier un : 2.
Le nombre entier deux : 4.
Le nombre entier quatre : 8.
Le nombre entier huit : 16.
Et pourquoi pas le nombre entier cent : 200.
Cette egalité est vraie : 500 x 6 = 1500.
Et on finit en donnant la date :
nous sommes le 44 Avril 4000.
c:\progs>
    

Un petit exercice moyennement compliqué.

On va lire une date sous la forme aaaa/mm/jj. L'année sera compriose entre 1900 et 2099.

On désire vérifier qu'elle est correcte, et en plus on indiquera si c'est une date du XX ème siècle (date<2000) ou du XXI ème siècle (date>1999)

c:\progs> type date.plent 
#!/usr/bin/perl
print ("Donnez moi une date sous la forme aaaa-mm-jj : ");
chop ($date=);
$M31 = "(0[13578]|1[02]).(0[1-9]|[12]\\d|3[01])";
$M30 = "(0[469]|11).(0[1-9]|[12]\\d|30)";
$Mf = "02.(0[1-9]|[12]\\d)";
$xx = $date =~/^(19)\d\d.($M31|$M30|$Mf)$/;
$xxi = $date =~/^(20)?\d\d.($M31|$M30|$Mf)$/;
$ancienne = $date =~/^(d{1,4}).($M31|$M30|$Mf)$/;
if ($xx) {
  print ("$date est une date du XXème siècle qui semble valide.\n");
} elsif ($xxi) {
  print ("$date est une date du XXIème siècle qui semble valide.\n");
} elsif ($ancienne) {
  print ("$date n'est pas une date du XX eme siècle.\n");
} else {
  print ("$date ne semble pas etre une date valide.\n");
}
c:\progs>
  

Afin de faciliter l'ecriture et les explications, les modèles ont été stockés dans des variables.

La première forme.

(0[13578]|1[02]).(0[1-9]|[12
  

Affectée à la variable $M31, va permettre

(0[13578]|1[02]).(0[1-9]|[12]\\d|3[01]) De définir les mois de 31 jours (01, 03, 05, 07, 08, 10, 12). Ou bien un 0 suivi d'un des chiffres 1, 3, 5, 7, 8. Ou bien un 1 suivi d'un des chiffres 0, 2.
(0[13578]|1[02]).(0[1-9]|[12]\\d|3[01]) De sauter un caractère, le séparateur.
(0[13578]|1[02]).(0[1-9]|[12]\\d|3[01]) De définir tous les nombres de 2 chiffres compris entre 01 et 31.
Un 0 suivi d'un des chiffres 1 à 9.
Un 1 ou un 2 ([12]) suivi d'un des chiffres 0 à 9.
Un 3 suivi d'un des chiffres 0 ou 1.

On a donc établi un rapport entre les mois de 31 jours et le numéro du jour qui doit être compris entre 1 et 31.

La seconde forme.

(0[469]|11).(0[1-9]|[12]\\d|30)
  

Affectée à la variable $M30, fait la même chose pour les mois de 30 jours.

(0[469]|11).(0[1-9]|[12]\\d|30) Définition des mois de 30 jours (04, 04, 04, 11). Un 0 suivi d'un des chiffres 4, 6, 9. Les deux chiffres 11.
(0[469]|11.(0[1-9]|[12]\\d|30) De sauter un caractère, le séparateur.
(0[1-9]|[12]\\d|30)(0[1-9]|[12]\\d|30) De définir tous les nombres de 2 chiffres compris entre 01 et 3.
Un 0 suivi d'un des chiffres 1 à 9.
Un 1 ou un 2 ([12]) suivi d'un des chiffres 0 à 9.
Les deux chiffres 31.

Le rapport est maintenant fait entre les mois de 30 jours et le numéro du jour qui doit être compris entre 1 et 30.

Reste à traiter le cas particulier du mois de février, grâce au modèle.

02.(0[1-9]|[12]\\d)
  

Qui est affectée à la variable $M, et qui va permettre.

02.(0[1-9]|[12]\\d) De définir le mois 02, février.
02.(0[1-9]|[12]\\d) De sauter le séparateur
02.(0[1-9]|[12]\\d) Puis de définir tous les nombres de 2 chiffres compris entre 01 et 29.
Un 0 suivi d'un des chiffres 1 à 9.
Un 1 ou un 2 ([12]) suivi d'un chiffre.

Afin de ne pas trop compliquer le problème, nous avons volontairement ignoré le fait que le mois de février pouvait ne contenir que 28 jours.

Nous passons maintenant aux expressions régulières proprement dites.

$xx = $date =~/^(19)\d\d.($M31|$M30|$Mf)$/;
  

Teste si la date est du XX ème siècle, à savoir que les deux premiers caractères sont les chiffres 19 et que la suite correspond a l'une ou l'autre des définitions que nous avons données à propos des relations entre le mois et le nombre de jours.

$xxi = $date =~/^(20)?\d\d.($M31|$M30|$Mf)$/;
  

Teste si la date est du XXI ème siècle, à savoir que les deux premiers caractères sont les chiffres 20 et que la suite correspond a l'une ou l'autre des définitions que nous avons données à propos des relations entre le mois et le nombre de jours.

$ancienne = $date =~/^(d{1,4}).($M31|$M30|$Mf)$/;
  

Teste simplement si la date est correcte, à savoir que les quatre premiers caractères sont des chiffres et que la suite correspond a l'une ou l'autre des définitions que nous avons données à propos des relations entre le mois et le nombre de jours.
On indique alors qu'il s'agit d'une ancienne date, antérieure à 1900

Si, en fin de compte, aucun des tests ne s'est avéré positif, on en déduit que l'expression de la date est incorrecte.

Précédent
Suivant