Cours n° 15, 16 février 2012
Expressions régulières en Perl
- Le
point et son interprétation
- Autres
flags (ou suffixes, ou options)
- Exemple
d'utilisation du point et de l'option e
- Scénario
(histoire vraie)
- Tâche
à effectuer
- Principe
d'une solution
- Réalisation
en Perl
- Exercices
- Dans le
prolongement de l'examen
de janvier 2009
- Améliorer
certains textes dans Frantext
- Le méta-caractère "
.
"
désigne un caractère quelconque, mais qui n'est pas
le saut de ligne "\n
".
Son emploi est commode quand on ne veut pas se casser la tête pour
spécifier toutes les classes de caractères
qui peuvent se trouver
à un
endroit donné.
On en trouvera un exemple d'emploi plus loin.
- La restriction relative à "
\n
"
s'explique par le fait qu'en règle générale les e.r.
respectent la structure de ligne.
Rappelons à ce propos que la ligne de texte reste
un unité irremplaçable dans la communication entre l'homme et la
machine.
Par exemple, lorsqu'un logiciel détecte une erreur dans un texte,
l'utilisateur est bien aise qu'on lui dise à quelle ligne
se trouve cette erreur !
D'autre part, tous les langages de programmation modernes proposent des
dispositifs pour lire un fichier ligne à ligne
(en Perl, l'opérateur "<>
",
en PHP la fonction file
, en Java la méthode readLine()
).
Il n'est donc pas surprenant que la portée d'une e.r. soit normalement
limitée à une ligne,
et que par conséquent le caractère "\n
" garde
un statut exceptionnel.
- Cette restriction peut être levée par l'emploi du flag
s
: voir plus loin.
Rappelons qu'il s'agit d'indicateurs placés après le dernier séparateur
(le second pour un filtrage, le troisième pour une substitution),
dont la présence modifie le comportement du moteur d'e.r. (cf. cours
n° 8).
Nous
avons déjà vu le suffixe g
(comme global) qui applique le filtrage ou la
substitution à toutes les sous-chaînes maximales
fitrées par l'e.r.
En voici quelques autres :
- Le suffixe
i
supprime la distinction majuscules/minuscules dans le filtrage : il le
rend insensible à la casse
en anglais case-insensitive - d'où son nom.
Exemple : le test $chn =~ m/0x([0-9a-f]+)/i
est
vrai aussi bien pour $chn
= "0xABC"
que pour $chn
= "0Xabc"
,
alors que pour $chn =~ m/0x([0-9a-f]+)/
(sans le suffixe i
)
il faut, par exemple, $chn
= "0xabc"
.
- Le suffixe
m
modifie la signification des ancres "^
"
et
"$
" (cf. cours
n° 9).
Il indique que la chaîne traitée comporte plusieurs lignes (m
comme multiligne), et il fournit le moyen de gérer
cette structure :
"^
" marque un début de
ligne et "$
" une fin de
ligne (au lieu du début et de la fin de la chaîne toute entière) -
et le point ne filtre toujours pas "\n
".
Autre formulation :
ce suffixe a pour effet d'appliquer le filtrage (ou la substitution) ligne
à ligne sur une chaîne de plusieurs lignes ;
il s'emploie donc volontiers en conjonction avec le suffixe g
.
- Le suffixe
s
modifie la signification du point.
Au contraire de m
, il
indique que la chaîne traitée doit être vue comme une seule ligne.
"^
" marque le début de la
chaîne, "$
" la fin de la
chaîne - comme d'ordinaire - et le point filtre "\n
".
Voici un double exemple pour illustrer le fonctionnement de
ces deux
suffixes antagonistes,
adapté de la page 'multiline' or 'single string' in
regex? :
my $chn1 = "Les sanglots longs\ndes
violons\nde
l'automne bercent mon cœur\nd'une langueur\nmonotone";
my $chn2 = $chn1; # deux exemplaires de la même chaîne
my $er = '^(.*)$';
$chn1 =~ s/
$er
/**$1--/mg;
print $chn1;
print "\n---------\n";
$chn2 =~ s/$er/**$1--/s;
print $chn2;
envoie en sortie :
**Les sanglots longs--
**des violons--
**de l'automne--
**bercent mon cœur--
**d'une langueur--
**monotone--
---------
**Les sanglots longs
des violons
de l'automne
bercent mon cœur
d'une langueur
monotone--
Note 1 : exécutez le code, et faites varier les suffixes pour vous
assurer que vous avez tout compris !
Note 2 : ces vers se trouvent au début de la chanson
d'automne de Verlaine. Ils ont servi à annoncer le débarquement
du 6 juin 1944 en Normandie.
- Le suffixe
e
ne s'applique qu'à une substitution : il indique que la chaîne
substituée ne doit pas être prise verbatim,
mais qu'elle doit être vue comme une expression
Perl, et que c'est la valeur de cette expression
qui doit être substituée à la sous-chaîne filtrée
(e
comme évaluer).
Notamment, cela permet d'appliquer une fonction aux valeurs des
variables spéciales $1
, $2
,
etc.
Voyez un exemple ci-après.
Plusieurs suffixes peuvent être employés simultanément (sauf m
et
s
, qui sont incompatibles).
Sylvain Lhullier donne l'exemple
suivant :
$s =~ s/0x([0-9a-f]+)/hex($1)/gei;
transforme tous les nombres hexadécimaux en nombres décimaux
dans la [chaîne valeur de la]
variable $s.
Application : comment faut-il modifier le code du §3 ci-dessus pour
obtenir la sortie suivante ?
**LES SANGLOTS LONGS--
**DES VIOLONS--
**DE L'AUTOMNE BERCENT MON CœUR--
**D'UNE LANGUEUR--
**MONOTONE--
C'est très simple !
-
Une petite société produit et vend un logiciel dans l'interface
utilisateur duquel apparaissent de nombreux messages en français.
Pour vendre son logiciel à l'étranger, notamment en Grèce, elle dispose
d'un fichier XML où figurent toutes les phrases-clefs du système
ainsi que leurs traductions. Le codage des caractères de ce fichier est
Latin-1, ce qui pose un problème pour le grec.
En fait, les textes grecs ont été saisis en IS0-8859-7, ce qui les rend
illisibles - mais l'information est préservée.
Voici un extrait du fichier tel qu'il apparaît dans un éditeur de
textes en Latin-1 : fichier ExGrec.xml
<PhraseClef id="22"
texte="Acquisition /
Modification / Présentation de divers paramètres"/>
..........
<PhraseClef id="758" texte="Veuillez indiquer votre mot de
passe"/>
................
<TraductionPhrase id="22" texte
="ÅéóáãùãÞ/Ðáñáìåôñïðïßçóç äéáöüñùí ðáñáìÝôñùí"/>
..........
<TraductionPhrase id="758" texte ="Äùóôå ôïí êùäéêü
óáò"/>
-
On demande de recoder le fichier en UTF-8, avec naturellement la
restitution des caractères grecs.
Notre extrait deviendra : fichier TradGrec.xml
<PhraseClef id="22"
texte="Acquisition / Modification / Présentation de divers
paramètres"/>
..........
<PhraseClef id="758" texte="Veuillez indiquer votre mot de
passe"/>
................
<TraductionPhrase id="22" texte
="Εισαγωγή/Παραμετροποίηση διαφόρων παραμέτρων"/>
..........
<TraductionPhrase id="758" texte
="Δωστε τον κωδικό σας"/>
-
Passer du codage ISO-8859-7 à Unicode est facile : en effet, la
séquence des numéros Unicode dans le bloc Greek
and Coptic
suit exactement l'énumération
des caractères grecs dans la plage B5-FE
de la
table ISO, à savoir :
Ά, ·, Έ, Ή, Ί, », Ό, ½, Ύ, Ώ, ΐ, Α, Β, Γ, Δ, Ε, Ζ, Η, Θ, Ι,
Κ, Λ, Μ, Ν, Ξ, Ο, Π, Ρ, -,
Σ, Τ, Υ, Φ, Χ, Ψ, Ω, Ϊ, Ϋ, ά, έ, ή, ί, ΰ, α, β, γ, δ, ε, ζ, η, θ, ι, κ,
λ, μ, ν, ξ, ο, π, ρ, ς, σ, τ, υ, φ, χ, ψ, ω, ϊ, ϋ, ό, ύ, ώ
Les caractères '»', '½', et la lacune marquée '-' correspondent à des
sauts dans les numéros Unicode.
Évidemment, ce n'est pas un hasard ! La norme ISO date de 1987, la
première version d'Unicode remonte à 1991,
l'une et l'autre reprennent
la norme nationale grecque ELOT 928 de 1986. D'où le parallélisme
observé, que nous allons exploiter.
Sachant que le numéro Unicode du caractère 'GREEK CAPITAL
LETTER ALPHA WITH TONOS
', premier caractère de
l'énumération, est 902 (0x386),
et que dans la table ISO son octet est B6
=
182, on en déduit la formule suivante pour calculer le numéro Unicode
d' un caractère grec :
soit k la valeur numérique de
l'octet qui code ce
caractère en ISO-8859-7, le rang de ce caractère dans l'énumération
ci-dessus est égal à k-182,
et son numéro Unicode est k-182+902
= k+720.
-
Elle se fait en deux temps :
- Écriture d'une fonction convertissant en caractères
grecs une chaîne de caractères lue en Latin-1, mais provenant
d'ISO-8859-7.
Appelons-la iso2uni
.
- Mise en œuvre de cette fonction via une expression
régulière avec l'option
e
, dans une
boucle
de lecture du fichier ligne à ligne.
Voici l'instruction essentielle de la boucle :
$ligne =~ s/(<TraductionPhrase
id="\d+" texte =")(.*)("\/>)/$1.iso2uni($2).$3/e;
- On emploie trois paires de parenthèses pour alléger
l'écriture, mais
seule la deuxième est indispensable pour fournir son argument à
iso2uni
.
- Le recours au point permet d'accepter aussi bien
les lettres que
d'éventuels chiffres, espaces ou ponctuations :
tout ce qui se trouve entre les guillemets est bon à prendre !
Réalisation de la fonction iso2uni
: on tient
compte de la possible présence de
caractères ASCII
sub iso2uni($){ # prend une chaîne en grec
ISO lu comme du Latin-1 et la renvoie en grec (Unicode)
my($gr) = @_;
my $res="";
my @tabcar = split(//, $gr);
foreach my $car ( @tabcar ){
my $k =
ord($car); # entier
if( $k
< 128 ){ # ASCII
$res .= $car;
}else{ #
caractère grec
$res .= chr($k+720);
}
}
return $res;
} #iso2uni
Réalisation de la boucle : il faut bien prendre
garde à l'ouverture des
deux fichiers :
- en entrée, avec le codage Latin-1
- en sortie, avec le codage utf-8.
En outre, il faut se méfier de la réalisation du saut de ligne : dans
le fichier d'entrée, qui vient de Windows, c'est CR+LF
et dans le fichier de sortie on le normalise en LF conformément à la
norme XML.
sub tradGrec($$){ #deux noms de fichier,
source et but
my($fichIn, $fichOut) = @_;
open(ENTREE,
"<:encoding(iso-8859-1)", $fichIn);
open(SORTIE, ">:encoding(UTF-8)",
$fichOut);
my $ligne = <ENTREE>; #
sauter l'en-tête XML
print (SORTIE '<?xml
version="1.0" encoding="utf-8"?>'."\n");
while( my $ligne =
<ENTREE> ){
chomp($ligne); # LF
chop($ligne); # CR
$ligne =~
s/(<TraductionPhrase id="\d+" texte
=")(.*)("\/>)/$1.iso2uni($2).$3/e;
print
(SORTIE "$ligne\n");
}
}#tradGrec
Le texte
complet.
-
- Éliminer les ponctuations répétées (du genre ",," ou
",;",
etc).
Attention au point qui marque une abréviation : "Je vous l'ai
envoyé par F.T.P., mais vous n'avez pas répondu !"
- Veiller à la présence d'une majuscule après un point
(même
remarque).
-
Certains textes du corpus Frantext portent la marque
de l'époque où ils ont été saisis :
- le e dans l'o '
œ
'
remplacé par le digramme 'oe
'
(coexistence
est correct, mais coeur
est une faute d'orthographe).
- les lettres accentuées majuscules remplacées par des
minuscules : "
DéDICACE
"
On se propose d'écrire un programme Perl qui rectifie ces errements,
aujourd'hui inacceptables.
En voici un,
adaptez-le à votre convenance !