Expressions régulières - Variable $_

 

Rassure-toi il ne s'agit que d'une introduction aux expressions régulières !

D'une façon générale comme pour les notions élémentaires on ne dira pas tout... mais on en dira un peu plus. Pour tout savoir sur un sujet il faut consulter la doc officielle.

Mais revenons à nos "expressions régulières". Pour te donner une idée de leur importance, figure-toi que j'ai lu quelque part que "L'une des possibilités les plus utiles en perl (sinon LA plus utile) était la puissance de manipulation des chaînes de caractères."

Ce qui confère au langage perl cette puissance, ce sont les expressions régulières. Tu trouveras également le terme "expressions rationnelles" qui est une autre manière de traduire l'anglais "regular expression".

Une expression régulière est une sorte de mélange entre un mini-programme et un mini-traitement de texte. Elle est contenue par des slash "/", et l'application à une chaîne de caractères se fait grace aux opérateurs =~ et !~ . Il existe des expressions régulières d'une complication, (et d'un hermétisme !) épouvantable. Aussi, attention ! On peut faire beaucoup de choses avec, mais également passer de longues heures à la mise au point et poser des casses-têtes effroyables à ses collègues. Ce qui est certain c'est qu'il faut documenter et faire des tests complets, en prenant bien soin des limites (champs vides et maxi) car bien souvent ce que l'on a péniblement écrit ne fait pas du tout ce que l'on croyait.

Pour ma part ayant déjà travaillé avec des expressions régulières (et m'étant délecté avec), je serai assez tenté de croire qu'un "vrai perliste" se fait un point d'honneur à utiliser les plus complètes (donc les plus compliquées !) possibles... Mais je suis certainement médisant en pensant cela... :o)

 

Pour l'instant on va commencer simple.

D'abord l'opérateur =~ Il applique l'expression régulière de droite sur la chaîne de gauche. Par exemple:
$phrase =~ /chat/ va rechercher si la variable $phrase contient la chaîne 'chat', si oui le résultat sera vrai (valeur 1) sinon il sera faux (chaîne vide '').

Faisons de suite un sort à l'opérateur contraire !~ Pour reprendre notre exemple : $phrase!~/chat/ sera vrai si la phrase NE contient PAS la chaîne "chat".

Attention les expressions régulières sont sensible à la casse : chercher "CHAT" n'est pas pareil que chercher "chat".

 

La variable $_ ou $ARG

C'est une variable spéciale prédéfinie : la variable de stockage par défaut. C'est aussi un espace de travail utilisé dans les recherches de motifs. Dans certain cas l'omission est autorisée et permet une simplification de l'écriture. Par exemple les expressions suivantes sont équivalentes :

  • $_=~/lune/ s'écrit simplement /lune/
  • chop($_) s'écrit chop tout court

Voici quelques endroits où $_ s'utilisera de façon implicite :

  • Les opérations de recherche m//, s///, et tr/// quand elles n'utilisent pas l'opérateur =~ ou !~
  • Diverses fonctions, notamment ord() et int()
  • Diverses fonctions de liste comme print() et unlink()
  • La variable d'itération par défaut dans une boucle foreach si aucune autre variable n'est précisée
Pour les curieux on va expliquer un peu plus précisemment ces nouvelles fonctionalités :
m/.../
Nous venons d'en parler dans le paragraphe précédent qui introduisait les expressions régulières. m/.../ peut tout simplement s'écrire /.../. m est l'abréviation de match (chercher)
s/.../.../
Va rechercher une chaîne de caractère et la remplacer par une autre. s est l'abréviation de substitute (remplacer).
tr/.../.../
Va remplacer toutes les apparitions de caractères par d'autres. tr est l'abréviation de translate (traduire).
ord()
Retourne la valeur ASCII du 1er caractère de la chaîne $_
int()
Retourne la partie entière du nombre décimal contenu dans $_
print()
Imprime la valeur de $_
 

La recherche de motifs (pattern)

 

Détaillons l'expression de recherche m/..../ (Attention on va souffrir un peu... :o))

Syntaxe générale : m/PATTERN/cgimosx

m est l'action,
'/' le délimiteur de champ,
PATTERN le masque de recherche
cgimosx des options.

  • Si '/' est le délimiteur alors m peut être omis

  • Si m est utilisé on peut alors choisir un délimiteur quelconque ni blanc ni alphanumérique. Il suffira de remplacer la paire de '/' par une nouvelle paire pour borner le pattern (éviter néanmoins ? qui est un cas particulier). Attention le délimiteur ' (apostrophe) supprime la traduction des variables dans le pattern.

  • Les variables seront traduites par leur contenu à chaque évaluation du pattern sauf si celui-ci est délimité par '
    Néanmoins il est possible d'éviter les compilations dynamiques pour gagner du temps en rajoutant /o à droite. Cette option doit être manipulée avec précaution en particulier dans le cas d'utilisation de variables dans le PATTERN.

  • Si le PATTERN est évalué comme un champ vide c'est la dernière expression régulière qui aura été trouvée vraie qui sera utilisée.

 

^ et $ début et fin de lignes/chaînes

^   Reconnait un début de ligne ou de chaîne.
$   Reconnait la fin d'une ligne ou d'une chaîne.
$_="Demain j'irai a la peche aux moules";
if (/^Demain/) {print "OK\n";} else {print "KO\n;"}
if (/peche/)   {print "OK\n";} else {print "KO\n;"}
if (/^peche/)  {print "OK\n";} else {print "KO\n;"}
OK. La chaîne commence bien par Demain
OK. La chaîne contient bien 'peche'
KO. Effectivement la chaîne ne commence pas par 'peche'

if (/moules$/) {print "OK\n";} else {print "KO\n;"}
if (/peche/)   {print "OK\n";} else {print "KO\n;"}
if (/peche$/)  {print "OK\n";} else {print "KO\n;"}
OK. La chaîne se termine bien par moules
OK. La chaîne contient bien 'peche'
KO. Effectivement la chaîne ne finit pas par 'peche'
 


Dans les exemples remarque le placement de ^ et $ par rapport à la chaine recherchée.

. * + ? présence de caractères

. (point)    Reconnait un caractère quelconque excepté les retours chariot.
*   Reconnait un nombre quelconque de fois (y compris zéro) le caractère qui précède.
+   Reconnait un nombre quelconque de fois (sauf zéro) le caractère qui précède.
?   Reconnait zéro ou une fois le caractère qui précède.
$l="Lundi j'irai a la peche aux moules";
$m="Mardi j'irai a    la peche aux moules";
$v="Vendredi j'irai ala peche aux moules";
if ($l=~/^...di/) {print "OK\n";} else {print "KO\n;"}
if ($m=~/^...di/) {print "OK\n";} else {print "KO\n;"}
if ($v=~/^...di/) {print "OK\n";} else {print "KO\n;"}
OK. La chaîne commence bien par trois caractères suivi de di
OK. Idem
KO. Pas là car après 'Ven' on ne trouve pas 'di'

if ($l=~/a *la/) {print "OK\n";} else {print "KO\n;"}
if ($m=~/a *la/) {print "OK\n";} else {print "KO\n;"}
if ($v=~/a *la/) {print "OK\n";} else {print "KO\n;"}
OK. La chaîne contient bien 'a' et 'la' séparés par un blanc
OK. La chaîne contient bien 'a' et 'la' séparés par plusieurs blancs
OK. La chaîne contient bien 'a' et 'la' séparés par rien du tout

if ($l=~/a +la/) {print "OK\n";} else {print "KO\n;"}
if ($m=~/a +la/) {print "OK\n";} else {print "KO\n;"}
if ($v=~/a +la/) {print "OK\n";} else {print "KO\n;"}
OK. La chaîne contient bien 'a' et 'la' séparés par un blanc
OK. La chaîne contient bien 'a' et 'la' séparés par plusieurs blancs
KO. La chaîne contient bien 'a' et 'la' mais séparés par rien du tout et comme il faut au moins un blanc de séparation....

if ($l=~/a ?la/) {print "OK\n";} else {print "KO\n;"}
if ($m=~/a ?la/) {print "OK\n";} else {print "KO\n;"}
if ($v=~/a ?la/) {print "OK\n";} else {print "KO\n;"}
OK. La chaîne contient bien 'a' et 'la' séparés par un blanc
KO. La chaîne contient bien 'a' et 'la' mais séparés par plusieurs blancs or il n'en faut qu'un ou pas du tout...
OK. La chaîne contient bien 'a' et 'la' séparés par rien du tout
 


Alors à ton avis que représentent
/^ *$/ (Solution)
/^.*$/ (Solution)

La substitution de caractères s/..../..../

 

Syntaxe générale : s/PATTERN/CIBLE/egimosx

s est l'action,
'/' le délimiteur de champs
PATTERN le masque de recherche
CIBLE chaîne de substitution
egimosx des options.

  • Recherche le motif dans une chaîne puis, s'il est trouvé, le remplace par le texte de CIBLE et retourne finalement le nombre de substitutions effectuées. Sinon, renvoie faux (en l'espèce, la chaîne vide).

  • On peut choisir un délimiteur quelconque ni blanc ni alphanumérique. Il suffit de remplacer la paire de '/' par une nouvelle paire pour borner le pattern. Attention le délimiteur ' (apostrophe) supprime la traduction des variables dans le pattern.

  • Les variables seront traduites par leur contenu à chaque évaluation du pattern sauf si celui-ci est délimité par '
    Néanmoins il est possible d'éviter les compilations dynamiques pour gagner du temps en rajoutant /o à droite. Cette option doit être manipulée avec précaution en particulier dans le cas d'utilisation de variables dans le PATTERN.

  • Voici quelques options :
    • g   Substitution globale, i.e. toutes les occurrences.
    • i   Motif indépendant de la casse (majuscules/minuscules).
    • o   Compilation du motif uniquement la première fois.


 

{n} {n,} {n,m} présence de caractères (suite)

{n}   Reconnait n fois exactement le caractère qui précède.
{n,}   Reconnait au moins n fois le caractère qui précède.
{n,m}   Reconnait au moins n fois le caractère qui précède, mais pas plus de m fois.
#!usr/bin/perl -w
$p1="brrrrr! il fait un froid de canard";$p0=$p1;
$p1=~s/r{2}//;
print "avant:$p0\napres:$p1\n";

avant:brrrrr! il fait un froid de canard
apres:brrr! il fait un froid de canard

Et avec l'option g (globale) on obtient :

$p1=~s/r{2}//g;

avant:brrrrr! il fait un froid de canard
apres:br! il fait un froid de canard
 









Tu remarqueras l'utilisation d'une chaine vide comme CIBLE.

#!usr/bin/perl -w
$p1="brrr! il fait un froid de canard";$p0=$p1;
$p1=~s/r{4,}//;
print "avant:$p0\napres:$p1\n";

avant:brrr! il fait un froid de canard
apres:brrr! il fait un froid de canard
 





Normal, on veut enlever 4 fois le caractère r et il n'y en a que 3...Rajoutes en un, puis deux, puis etc... et teste.
#!usr/bin/perl -w
$p1="brrr! il fait un froid de canard";$p0=$p1;
$p1=~s/r{2,4}//;
print "avant:$p0\napres:$p1\n";

avant:brrr! il fait un froid de canard
apres:b! il fait un froid de canard
 

[...] Classes de caractères

Les crochets [] sont utilisés pour assortir n'importe lequel des caractères qu'il contiennent. A l'intérieur des crochets, "-" signifie : entre et "^" signifie : pas. Par exemple:
[alb]    sera vrai si la chaîne contient a ou l ou b
[^alb]  sera vrai si la chaîne ne contient ni a, ni l ni b
[a-z]   sera vrai la chaîne contient une lettre minuscule
[^a-z] sera vrai si la chaîne ne contient pas de lettres minuscules
[a-zA-Z] sera vrai si la chaîne ne contient que des lettres minuscules ou majuscules
#!usr/bin/perl -w
$v="-1a457bd012";$w=$v;
$v=~s/[+\-0-9]/ /g;
$v=~s/[^ ]/^/g;
print "$w\n$v\n";

-1a457bd012
  ^   ^^

 

 

















Le premier substitute transforme tout ce qui est + - ou chiffre par un blanc
et le deuxième tout ce qui n'est pas un blanc par ^

La traduction ou translitération tr/..../..../

 

Syntaxe générale : tr/liste_recherche/liste_remplacement/cds

tr est l'action (traduction)
'/' le délimiteur de champs
liste de recherche une liste de caractères à traduire
liste de remplacement liste cible
cds des options.

  • Substitue chacune des occurrences des caractères de la liste recherchée par le caractère correspondant de la liste de remplacement. tr retourne le nombre de caractères remplacés ou supprimés.

  • On peut fournir les listes comme des intervalles de caractères par l'utilisation du tiret. Ainsi tr/A-J/0-9/ effectue les mêmes remplacements que tr/ACEGIBDFHJ/0246813579/ On conseille de n'utiliser les tirets qu'à l'intérieur d'un même alphabet (a-z ou A-Z) ou dans des séquences de chiffres 0-9

  • Il n'y a pas de substitution des variables à l'exécution. Pour la réaliser il faut utiliser eval()

  • Voici les options :
    • c   Complémente la liste de recherche.
    • d   Efface les caractères trouvés mais non remplacés.
    • s   Agrège les caractères de remplacement dupliqués.


  • Si le modificateur /c est utilisé, c'est le complément de la liste recherchée qui est utilisé. (complément=le reste des caractères)
  • Si le modificateur /d est spécifié, tout caractère spécifié dans LISTE_RECHERCHEE et sans équivalent dans LISTE_REMPLACEMENT est effacé.
  • Si le modificateur /s est spécifié, les suites de caractères qui sont remplacés par le même caractère sont agrégées en un seul caractère.
  • Si le modificateur /d est utilisé, LISTE_REMPLACEMENT est toujours interprété exactement comme spécifié. Sinon, si LISTE_REMPLACEMENT est plus court que LISTE-RECHERCHEE, le dernier caractère est répété autant de fois que nécessaire pour obtenir la même longueur. Si LISTE_REMPLACEMENT est vide, LISTE_RECHERCHEE est utilisé à la place. Ce dernier point est très pratique pour comptabiliser les occurrences d'une classe de caractères ou pour agréger les suites de caractères d'une classe.
 

Quelques exemples :

$ph =~ tr/A-Z/a-z/;    # tout en minuscule

$cnt = tr/*/*/;        # compte les étoiles
                       # dans $_

$cnt = $sky =~ tr/*/*/;# compte les étoiles
                       # dans $sky

$cnt = tr/0-9//;       # compte les chiffres
                       # dans $_

tr/a-zA-Z//s;          # bookkeeper -> bokeper

($HOST = $host) =~ tr/a-z/A-Z/;

tr/a-zA-Z/ /cs;        # remplace tous les 
                       # non alphanumériques
                       # par un seul espace

tr [\200-\377]
   [\000-\177];        # efface le 8ème bit.