Perl
Perl
Avant propos
Ceci n'est qu'une introduction au Perl, car nous pourrions écrire 4 ou 5 livres, voir plus, sur ce sujet ! ;-) Donc ne vous attendez pas à faire le tour de la question...
Et je précise aussi, que ce n'est pas non plus un cours de programmation : cet article s'adresse à des personnes possédant des bases de programmation et voulant se mettre à Perl.
Présentation
Perl est un langage de programmation extrêmement efficace pour traiter les fichiers et les chaînes de caractères. Venant du monde Unix, il intègre toutes les fonctions que l'on peut retrouver en shell avec des commandes comme grep, sed, awk, ... Sa syntaxe générale ressemble beaucoup à celle du C et du shell. De plus, il est très apprécié des administrateurs système afin de gérer les fichiers de log.
Note de Jice : Perl est un langage très polyvalent. LE jeu, j'ai nommé Frozen Bubble, est même programmé en Perl !
Les particularités de Perl
- C'est un langage compilé et interprété. C'est à dire que l'on écrit le script, un simple fichier texte, puis à chaque fois que nous exécutons le script, l'interpréteur compile le code à la volée sous une forme intermédiaire et exécute les commandes.
On pourrait ainsi croire qu'un script Perl est moins performant qu'un programme C compilé, mais il s'avère souvent plus performant et plus facile de faire un programme en Perl qu'en C. - Ce langage est disponible sur quasiment toutes les plates-formes et tous les systèmes possibles, Linux, Unices, Mac OS... Donc nos scripts Perl fonctionneront sur toutes ces machines sans aucune adaptation (à moins d'utilisation de spécificités d'un système d'exploitation). Ce qui rend Perl disponible sur autant de systèmes, c'est ses licences. Perl est Libre et est sous la licence GPL ou Artistic au choix.
- Perl est un langage qui permet la programmation orientée objet. Cet aspect du langage dépasse largement le cadre de cet article.
L'installation
Dans cette partie, je n'aborde que l'installation de Perl sur un système Linux, et ce quelle que soit la distribution. Enfin, j'espère... ;-)
Vérifions si Perl est présent
Il y a des chances pour que Perl soit déjà installé avec notre distribution ou que celle-ci fournisse un package Perl. Pour vérifier si Perl est déjà sur notre système faisons, en console :
perl -v
Ceci doit nous retourner un message du genre:
This is perl, v5.6.1 built for i386-linux
Copyright 1987-2001, Larry Wall
Perl may be copied only under the terms of either the Artistic licence or the GNU General Public licence, which may be found in the Perl 5 source kit.
Complete documentation for Perl, including FAQ lists, should be found on this system using `man perl' or `perldoc perl'. If you have access to the Internet, point your browser at http://www.perl.com/, the Perl Home Page.
Si c'est le cas, nous pouvons sauter le paragraphe suivant.
Installation de Perl
Pas de chance... Donc allons voir si notre distribution fournit un package et si oui, installons-le. Sinon...
Les binaires sont accessibles ici : http://www.activestate.com/Products/Download/Get.plex?id=ActivePerl&_x=1
Note : je viens de remarquer que maintenant il demande e-mail, prénom et nom pour télécharger Perl.
Les sources sont accessibles ici : http://www.perl.com/pub/a/langage/info/software.html#sourcecode
Maintenant détarons le package
tar -xvzf <votre package>
Allons sous le répertoire créé.
Si nous souhaitons choisir nos paramètres en répondant à des questions :
./Configure -Dprefix=/usr
make
En root (su)
make install
Sinon, pour une installation par défaut :
./Configure -Dprefix=/usr -d -e
make
En root (su)
make install
Vérifions que Perl est bien installé
perl -v
Introduction au langage Perl
Les principales règles du Perl
- Les lignes de code se terminent par un point virgule.
- Les noms de variables doivent être précédés par un caractère pour les identifier :
- une variable scalaire (nombre, chaîne de caractères...) est identifié par le symbole '$', par exemple $chaine,
- un tableau par le symbole '@', par exemple @tableau, et $tableau[12] représente l'élément n° 12. Un tableau est toujours unidimensionnel, c'est pourquoi on parle aussi de liste (voir plus bas pour les [#structurescomplexes tableaux multidimensionnels]). Afin de définir un tableau, on écrit : @tableau = (val1, val2, ... , valn);
- un hachage (hash en anglais) est identifié par le symbole '%', par exemple %hash. Un hachage une structure indexée par des chaînes de caractères. $hash{'prenom'} et $hash{'nom'} serviront à enregistrer le prénom et le nom de quelqu'un de façon explicite. Un hachage est défini de la manière suivante : %hash = ( prenom => 'jice', nom => 'cardot' );
- La déclaration des variables est non typée. C'est à dire que nous n'avons pas besoin de dire si cette variable va contenir une chaîne de caractère ou un entier. D'ailleurs, une même variable pourrait contenir à un moment un entier et à un autre une chaîne, mais ce n'est pas conseillé pour la relecture du code.
- Il y a deux sortes de chaînes de caractères. La chaîne de caractère définie entre simples cotes (') n'est pas interpolée. La chaîne de caractères définie entre guillemets (") est interpolée. Lorsque le Perl interprète une chaîne, il va regarder le contenu de celle-ci pour remplacer les variables qu'il trouvera par leurs valeurs.
Notation : q(chaîne) est synonyme de 'chaîne' et qq(chaîne) de "chaîne". Remarquons que les parenthèses dans cette notation peuvent être remplacées par tout autre caractère, comme par exemple qq|chaîne| ou qq!chaîne!. - La déclaration des variables n'est pas obligatoire. Il y a une commande pour nous obliger à déclarer les variables et d'autres choses.
use strict;
Je conseille très fortement d'utiliser ceci, afin d'écrire un code source plus propre et lisible. En effet, cette directive impose de déclarer les variables, et interdit certaines constructions dangereuses... Ceci facilite la recherche d'erreurs, et le débutant prend ainsi dès le début de bonnes habitudes de programmation. - Un bloc est une suite d'instructions, séparées par des points virgules, et entourée d'accolades. Par exemple :
{
$toto = 12;
print $toto;
}
est un bloc.
Définir l'interpréteur
Le fait de définir l'interpréteur dans le script permet de l'exécuter directement. Pour ce faire il faut placer la ligne suivante au tout début du script :
#!/usr/bin/perl
Ce qui permettra d'exécuter le script directement par :
./script.pl
Sans cette ligne il faudrait exécuter le script comme ceci :
perl script.pl
En ajoutant l'option -W, nous obtenons la liste des toutes les erreurs et de tous les avertissements lors de l'interprétation du script.
Les commentaires
Un commentaire débute par un caractère dièse (#). Tout ce qui suit ce caractère jusqu'à la fin de la ligne sera considéré comme du commentaire.
Par exemple :
# Ceci est un exemple de commentaire
print "test"; # un 2ème commentaire
Utiliser une libraire particulière
L'extension des fichiers librairies (ou module) est pm. pm signifie Perl Module.
Pour appeler une librairie, on ajoute la directive :
use la_librairie;
Pour indiquer à perl où chercher nos librairies personnelles, on utilise la directive :
use lib "le chemin d'accès";
Remarque : les chemins de recherche des librairies sont définis dans le tableau @INC
Déclarer une variable
Variable globale :
Cela se fait automatiquement lors de la première utilisation de la variable. Cependant, lorsqu'on utilise use strict, il faut déclarer les variables globales par la directive suivante : use vars qw($var1 $var2 @tab1 %hash1);
Remarque : le mot clef local s'applique à une variable globale (local $variable;). Il permet à l'intérieur d'un bloc de cacher une variable globale par une autre valeur. La variable reste globale, c'est à dire que dans les procédures appelées dans le bloc, la nouvelle valeur est utilisée ; à la fin du bloc, l'ancienne valeur est restaurée.
Variable lexicale :
Une variable lexicale n'existe qu'à l'intérieur d'un bloc. On parle aussi de variable locale.
On la déclare par my $variable ou my @tableau...
Pour déclarer plusieurs variables et un tableau en une seule commande, faire :
my ($var1, $var2, @tab);
Les opérateurs
Voici les opérateurs les plus couramment utilisés. Pour plus d'info sur les opérateurs : man perlop
Opérateurs unaires
! | : négation logique. Si $a vaut vrai, !$a vaudra faux. |
- | : négation arithmétique. Si $a vaut 2, -$a vaudra -2. |
~ | : négation binaire ou complément à 1. |
++ | : incrémentation ; si ++ est placé avant la variable, l'interpréteur l'incrémente avant de retourner la valeur, si ++ est placé après, il retourne la valeur puis l'incrémente. Exemple : $A++ |
-- | : décrémentation ; idem. |
Opérateurs binaires
+ - * / % ** | : opérations arithmétiques (% est le modulo, ** est l'exponentiation). |
. | : concatène deux chaînes : "aze"."rty" vaut "azerty". |
x | : "multiplie" une chaîne : "to" x 2 vaut "toto". |
& | ^ << >> | : opérations sur les bits : et, ou, ou exclusif (xor), décalage à gauche, à droite. |
&& || | : et logique, ou logique. |
Opérateur ternaire
?: | : cet opérateur particulier est nommé "opérateur conditionnel". Il fonctionne à la manière d'un si alors sinon. Si l'expression à gauche du ? est vraie, l'argument avant le : est retourné, sinon l'argument après le : est retourné. Mieux vaut un exemple : print (($n>1)?'s':''); affichera 's' si $n est plus grand que 1, et n'affichera rien ('') sinon. |
Opérateurs de comparaison numériques
< <= > >= | : plus petit, plus petit ou égal, plus grand, plus grand ou égal |
== != | : égal, différent |
Opérateurs de comparaison pour les chaînes de caractères
lt le gt ge | : plus petit, plus petit ou égal, plus grand, plus grand ou égal |
eq ne | : égal, différent |
=~ !~ |
: teste si une chaîne de caractères correspond à une expression régulière. Voir le paragraphe [#expressions_regulieres expressions régulières]. |
L'affectation d'une variable
L'opérateur d'affectation "normal" est '='. Par exemple :
$variable = mavaleur;
affecte la valeur mavaleur à la variable $variable.
D'autres opérateurs d'affectation sont un raccourci pour les opérations binaires.
Par exemple $a += 2 est un raccourci pour $a = $a + 2.
Les opérateurs d'affectation les plus couramment utilisés sont :
= | : affectation "standard" |
+= -= *= /= %= | : pour les opérations arithmétiques (+, -, *, / et modulo) |
&&= ||= != | : pour les opérations logiques (et logique, ou logique, non logique) |
&= |= ^= ~= <<= >>= | : pour les opérations logiques sur les bits (et, ou, ou exclusif, complément à 1, décalages gauche et droite) |
.= | : pour les opérations sur les chaînes de caractères (concaténation) |
=~ !~ |
: pour les expressions régulières (voir plus bas). |
Quelques variables particulières prédéfinies
use English; permet d'utiliser les noms des variables au lieu des noms standards des variables spéciales (ex : $ARG au lieu de $_).
@_ ou @ARG : Tableau d'arguments. Sert pour le passage de paramètres.
$_ ou $ARG : Variable par défaut des fonctions unaires entrées/sorties, recherche...
$/ ou $RS ou $INPUT_RECORD_SEPARATOR : fin de ligne lorsqu'on lit un fichier, \n par défaut sous unix et \r\n sous windows.
$! ou $ERRNO : suivant le contexte d'utilisation, retourne le numéro de la dernière erreur système, ou le message d'erreur système.
$0 ou $PROGRAM_NAME : Nom du fichier qui contient le script Perl.
$^0 ou $OSNAME : Nom du système d'exploitation.
Utiliser une variable d'environnement
$variable = $ENV{"nom de la variable d'environnement"}; Remarque, ce n'est que l'utilisation du hachage %ENV.
Pour plus d'information sur ces variables : man perlvar
Branchements conditionnels
Ce test classique peut s'écrire de la manière suivante :
if (test) {bloc}
if (test) {bloc} else {bloc}
if (test) {bloc} elsif (test) {bloc} ... else {bloc}
test étant toute expression qui retourne une valeur.
exemple :
if (!open(F,$FICHIER)) { die "impossible d'ouvrir $FICHIER: $!"; }
Il y a d'autres manières de faire la même chose, vous pouvez utiliser par exemple :
instruction if test; | exécute l'instruction si le test est vrai. exemple : die "impossible d'ouvrir $FICHIER: $!" if !open(F,$FICHIER); |
instruction unless test; | exécute l'instruction si le test est faux. exemple : die "impossible d'ouvrir $FICHIER: $!" unless open(F,$FICHIER); |
commande1 or commande2; | exécute la commande1 ; si elle retourne faux, alors la commande2 sera exécutée. Sinon on passe à la suite. exemple : open(F,$FICHIER) or die "impossible d'ouvrir $FICHIER: $!"; |
test?commande1:commande2; | exécute la commande1 si la condition du test est remplie, sinon il exécute la commande2. |
Note : Pour ceux qui connaissent, il n'y a pas d'équivalent au 'case' ou 'switch'. Il y a plusieurs façons de faire, notamment utiliser if elsif ... else.
man perlsyn vous donnera plus d'informations.
Les boucles
For et foreach
Il y a deux types de boucles for, le for 'style C', et le for/foreach Perl.
for/foreach Perl
Dans ce cas, for et foreach sont synonymes. On peut utiliser l'un ou l'autre pour améliorer la lecture, par habitude, etc.
Pour aller de 1 à 9 :
for $cpt (1 .. 9) {bloc} ou foreach $cpt (1 .. 9) {bloc}
(dans {bloc}, $cpt prend successivement les valeurs de 1 à 9)
ou
for (1 .. 9) {bloc}
ici, à chaque itération, la variable par défaut $_ prend la valeur suivante.
Pour parcourir chaque éléments d'un tableau :
foreach $variable (@tableau) {bloc}
A chaque itération, $variable prendra la valeur suivante de @tableau.
Si on omet $variable, alors la valeur de l'élément sera stocké dans la variable $_ :
foreach (@tableau) {bloc}
Remarque : l'écriture 1..9 définit en réalité un tableau de 9 éléments (1,2,3,4,5,6,7,8,9).
for 'style C'
La construction 'classique' à la C est possible, mais tellement moins Perl...
for ($cpt=1 ; $cpt < 10 ; $cpt++) {bloc}
Cette construction est néanmoins très puissante, et permet de faire beaucoup plus de choses que de simples itérations... A vous de voir !
A noter que dans les exemples ci-dessus, les variables $cpt ou $variable sont locales à la boucle. Si elles doivent être déclarées, vous pouvez les faire précéder du mot clef my :
for my $cpt (1 .. 9) {bloc}
for (my $cpt=1 ; $cpt < 10 ; $cpt++) {bloc}
While
Les boucles while se construisent comme suit :
while (test) {bloc1}
while (test) {bloc1} continue {bloc2}
Le bloc1 puis le bloc2 sera exécuté tant que le test est vrai.
L'exécution des boucles peut être pilotée à l'intérieur des blocs par les commandes next, last et redo.
- next lance l'itération suivante de la boucle. Le bloc continue est exécuté puis la boucle redémarre.
- last sort de la boucle, sans exécuter le bloc continue.
- redo relance l'itération de la boucle, sans exécuter le bloc continue ni le test.
Par exemple, le code suivant lit l'entrée standard, et ignore les commentaires (lignes commençant par #)
while (<STDIN>) {
next if /^#/;
...
}
Autre exemple, une itération de 1 à 9
$cpt = 1;
while ($cpt < 10) {
...
$cpt++;
}
ou bien :
$cpt = 1;
while ($cpt < 10) {
...
}
continue {
$cpt++;
}
Pour plus d'information sur ces constructions : man perlsyn.
Les fonctions et procédures
Déclaration des fonctions
Une fonction peut être déclarée n'importe où dans le script Perl, mais pour des questions de lisibilité, il est préférable de déclarer les fonctions regroupées soit en début, soit en fin de script.
On peut pré-déclarer les fonctions par :
sub mafonction;
ou les déclarer directement par :
sub mafonction {bloc}
Passage de paramètres
Les paramètres sont passés comme une liste de valeurs scalaires (nombre, chaîne...) à plat.
Par exemple :
ma_fonction($variable, "chaîne");
Pour lire valeur des paramètres passés dans la fonction, il suffit de lire la variable @_ qui est le tableau contenant les paramètres.
Une façon simple de les lire est d'écrire en début de fonction :
my ($variable, $chaine) = @_;
On peut aussi récupérer les arguments en dépilant les éléments du tableau @_ avec la commande shift.
my $variable = shift @_;
qui peut s'abréger en
my $variable = shift;
car le tableau @_ est l'argument par défaut de shift.
Les paramètres sont passés par valeur. Pour pouvoir modifier le contenu d'une variable passée en paramètre à une fonction, il faut passer à cette fonction une référence sur la variable (on parle de passage par référence). Une référence est un type scalaire particulier qui permet de se référer à une variable. La notation consiste à faire précéder la variable par un caractère '\'. Pour récupérer la variable cachée derrière la référence (ou déréférencer), il faut faire précéder la référence du caractère de type correspondant, et optionnellement entourer la référence d'accolades.
Exemple :
$a = 1;
$b = \$a; # $b est une référence à $a
$c = $$b + 1; # $c = 2
$c = ${$b} + 1; # synonyme
Démonstration :
sub test1 {
my $a=shift; # récupération du paramètre
$a += 1; # on ajoute un
}
sub test2 {
my $ref_a=shift;
$$ref_a += 1; # le premier $ déréférence la référence
}
$a = 1;
print $a; # affiche 1
test1($a);
print $a; # affiche 1
test2(\$a); # \$a est une référence sur $a
print $a; # affiche 2
Le passage par référence a aussi pour d'intérêt de pouvoir passer des tableaux ou des hachages en paramètres à une fonction. En effet, on a vu que les paramètres d'une fonction sont passés comme une liste de valeurs scalaires à plat. Si on désire passer 2 tableaux @tab1=(1,1,1) et @tab2=(2,2) à une fonction, et que l'on écrit :
mafonction (@tab1, @tab2);
alors perl traduira cela comme :
mafonction (1,1,1,2,2);
car il met toutes les valeurs à plat dans une liste. Une fois dans la fonction on sera bien embêté pour savoir distinguer les éléments du premier tableau de ceux du deuxième.
On va donc passer une référence sur chaque tableau :
mafonction (\@tab1, \@tab2);
Exemple :
sub liste_gt {
my ($l1ref, $l2ref) = @_;
$n1 = @$l1ref; # @$l1ref représente le tableau référencé par $l1ref
$n2 = @$l2ref;
# (affecter un tableau dans un scalaire retourne le nombre d'éléments)
if ($n1 > $n2) { return 1; }
else { return 0; }
}
@tab1 = (1,2,3);
@tab2 = ("a",2,"r",5);
if (liste_gt(\@tab1, \@tab2)) {
print "tab1 a plus d'éléments que tab2\n";
} else {
print "tab2 a plus d'éléments que tab1\n";
}
Tout cela s'applique de la même façon aux hachages.
Pour plus d'informations sur les références, voir man perlreftut (tutoriel) et man perlref (références).
Retour de valeurs
Une fonction peut renvoyer une ou des valeurs, grâce à la commande return :
sub ecrire
{
...
return $valeur;
... # renvoie une valeur unique
return $valeur1, $valeur2;
... # renvoie une liste de valeurs
}
Cette commande provoque la sortie immédiate du sous-programme pour retourner dans le programme appelant.
return permet de renvoyer une valeur ou une liste de valeurs. Cela permet donc à une fonction de retourner soit un tableau :
@resultat = ma_fonction;
soit plusieurs valeurs :
($a, $b) = ma_fonction;
De même que pour le passage de paramètres, la liste de valeur est une liste "à plat", ce qui signifie que pour retourner plusieurs tableaux, il faut également passer par les références :
($ref_a, $ref_b) = ma_fonction;
où $ref_a est une référence sur le tableau @$ref_a et où ma_fonction retourne une liste de deux références.
Pour plus d'information sur les fonctions de Perl, voir man perlsub.
Les expressions régulières, ou regex
Perl est un langage très pratique pour manipuler du texte. Un des éléments qui le rend si adapté à cet usage est la présence des expressions régulières, qui permettent de déterminer si une chaîne de caractère correspond à un masque prédéfini, d'extraire des éléments d'une chaîne de caractères suivant un masque, de remplacer des parties d'une chaîne suivant un masque, etc. C'est ce masque, défini sous forme d'une chaîne de caractères, que l'on appelle expression régulière.
Par exemple, $a =~ /lea/ teste si la chaîne "lea" est dans la chaîne $a, $a =~ /^lea/ si $a commence par "lea", $a =~ /l[eé]a/ si la chaîne $a contient "lea" ou "léa"...
De prime abord, ce sont des sortes de formules qui semblent ne rien vouloir dire, mais une fois que nous les maîtrisons, elles se révèlent extrêmement pratiques et puissantes.
Les éléments composant une expression régulière
Les éléments les plus utilisés sont :
- les caractères standards : majuscules, minuscules, chiffres, etc.
- les méta-caractères :
- . : représente un caractère quelconque
- ^ : début de la chaîne
- $ : fin de la chaîne
- () : groupement et capture (la chaîne qui correspond au masque présent entre les parenthèses est affectée à la variable $1 pour la première parenthèse, $2 pour la seconde, etc. On peut ensuite utiliser ces variables normalement, notamment dans la substitution, voir plus bas)
- (?:) : groupement sans capture (pas d'affectation aux variables $1, $2...)
- [] : classe de caractères (c'est à dire l'un des caractères présent entre les crochets ; on peut de plus spécifier des plages de caractères, par exemple [a-e] correspond à tous les caractères entre a et e ; [aeiou] aux voyelles ; [aeiou0-9] aux voyelles plus aux chiffres...)
- | : alternance (sorte de ou logique)
- \ : permet de protéger un caractère réservé (voir ci-dessus) lorsqu'on veut l'utiliser comme caractère standard dans la regex.
- les caractères spéciaux :
- \s : représente un espace
- \S : caractère autre que espace
- \n : saut de ligne
- \r : retour chariot (les lignes des fichiers Windows se terminent par \r\n, et par \n sous Unix). Pour faire du code portable, il vaut mieux utiliser $/ qui représente la fin de ligne (voir le paragraphe sur les variables spéciales).
- \t : tabulation
- \d : un chiffre (=[0-9])
- \w : alphanumérique (=[0-9a-zA-Z_] plus les accents, etc. si on précise use locale;)
- plus de nombreux autres...
- les quantifieurs (ils suivent un caractère ou un méta-caractère afin d'en représenter le nombre que l'on cherche) :
- * : de 0 à n
- + : de 1 à n
- ? : 0 ou 1
- {n} : exactement n
- {n,} : au moins n
- {n,m} : entre n et m par défaut, les quantifieurs sont "gourmands", c'est à dire qu'ils recherchent un maximum de caractères tant que le reste du masque correspond. Afin de rechercher un minimum de caractère, il faut faire suivre le quantifieur de '?', par exemple *?, {n,}?, etc.
- les assertions de largeur nulle (i.e. ne représentant pas un caractère)
- \b : correspond à une limite de mot
- \B : correspond à une non limite de mot (i.e. intérieur !)
- (?!pattern) : assertion négative en avant. Par exemple, /toto(?!titi)/ teste si la chaîne contient "toto", non suivi de "titi".
- (?<!pattern) : assertion négative en arrière. Par exemple, /(?<!titi)toto/ teste si la chaîne contient "toto", non précédé de "titi". La largeur de "titi" doit être fixe (i.e. toujours le même nombre de caractères).
- ...
Les différentes utilisations des expressions régulières
m/regex/ ou /regex/ (sans le m) teste si une chaîne correspond (match) à une regex donnée.
s/regex/substitution/ remplace la partie de la chaîne correspondant à la regex par la substitution.
Pour appliquer un test m// ou une substitution s/// à une chaîne de caractères, on utilise l'opérateur =~ ou !~.
Par exemple :
if ($s =~ /win(dows)?/) { kickban(); }
teste si la chaîne $s contient "win" ou "windows".
Si on ne précise pas de chaîne, le test se fait sur la variable par défaut $_ :
if (/win(dows)?/) { kickban(); }
teste si $_ contient "win" ou "windows".
L'opérateur !~ correspond quant à lui à la négation : $s !~ /coin/ teste si la chaîne $s ne contient pas "coin".
options
Les expressions régulières peuvent de plus être suivies de modifieurs ou options, qui en modifient le comportement, notamment :
- g : recherche globalement, i.e. ne s'arrête pas à la première occurrence.
- i : recherche insensible à la casse des caractères (i.e. majuscules/minuscules)
- m : traite la chaîne comme des lignes multiples (lorsqu'elle contient des retours à la ligne). ^ et $ correspondent dans ce cas à un début de ligne et une fin de ligne.
- s : traite la chaîne comme une seule ligne. Dans ce cas, le méta-caractère "." peut correspondre à "\n" ce qui n'est pas le cas normalement.
- x : permet d'insérer des espaces voire des commentaires dans un masque.
- e : uniquement pour s///, traite la substitution comme du code perl : elle est interprétée par perl avant d'être substituée.
Par exemple :
"Bonjour Léa" =~ m/jour/; # correspond
"Bonjour Léa" =~ m/Jour/; # ne correspond pas
"Bonjour Léa" =~ m/Jour/i; # correspond
groupement et capture
Si une partie d'une regex est entre parenthèses, alors la partie de la chaîne qui correspond à cette parenthèse sera affectée à la variable $1 (on dit "capturée"). Si une deuxième parenthèse est présente dans la regex, la variable $2 sera utilisée, et ainsi de suite. Si la parenthèse commence par ?: alors il n'y aura pas d'affectation à une variable $1, $2, etc.
Ainsi :
if (/win(?:dows)?\s(..)/i) {
if ($1 eq "xp") { kickban(); }
else { kick(); }
}
teste si $_ contient "win" ou "windows", suivi d'un espace, puis de 2 caractères, que l'on va capturer dans $1. La première parenthèse (?:dows) permet de regrouper les quatre caractères "dows" afin de leur appliquer le quantifieur '?' (0 ou 1).
Les captures $1, $2... peuvent être utilisées lors d'une substitution :
$s =~ /win(?:dows)?\s(..)/winblows $1/
Tester si une chaîne correspond à un masque donné : m// ou //
Par exemple, pour tester si une chaîne contient le mot "lea" (le i de la fin permet de ne pas différencier majuscules et minuscules) :
if (/lea/i) ...
Pour tester si une chaîne commence par la lettre 'a', on peut faire :
if (/^a/) ...
Pour tester si une chaîne se termine par 'z' :
if (/z$/) ...
Enfin pour tester si une chaîne contient un quadruplet héxadécimal (i.e. 4 chiffres de 0 à 9 ou A à F) :
if (/[0-9A-F]{4}/) ...
Ici, par défaut, c'est la valeur contenu dans $_ qui est utilisée pour le test. Et bien entendu, nous pouvons compliquer à loisir l'expression régulière, et utiliser le groupement (voir plus haut) afin de récupérer les valeurs trouvées dans les chaînes pour s'en servir par la suite.
Substituer une partie d'une chaîne à une autre : s///
Rappel : la syntaxe est s/regex décrivant l'élément à remplacer/élément remplaçant/options
Exemples :
$variable =~ s/a/b/;
Cette commande va remplacer le premier 'a' de la ligne par 'b'. Pour remplacer tous les 'a' faire :
$variable =~ s/a/b/g;
Si en plus nous voulons que cela ne fasse pas la différence entre les majuscules et les minuscules (case sensitive), faire
$variable =~ s/a/b/gi;
Pour remplacer tous les ensembles de caractères commençant par 'a', finissant par 'e' et avec au moins un caractère au milieu sans être sensitif par 'toto', faire :
$variable =~ s/a.*e/toto/gi;
La traduction : tr///
Cette construction "à la regex" permet de remplacer des caractères par d'autres dans une chaîne, à la manière de la commande unix 'tr'.
La syntaxe est tr/éléments à traduire/éléments traduits/
Ou y/éléments à traduire/éléments traduits/
Exemple :
$variable =~ tr/a-b/A-B/;
Cela va transformer tous les 'a' et 'b' minuscules en majuscules.
Plus d'info sur :
man perlre
man perlretut (le tutoriel sur les regex, une bonne lecture !)
Structures de données complexes
Tableaux et tables de hachage multidimensionnels
On a vu plus haut que les tableaux Perl ne possèdent qu'une seule dimension (il ne peuvent contenir que des éléments de type scalaire). On a aussi vu plus haut l'existence de références, type scalaire particulier qui permet de se référer à une variable, donc en l'occurence à un tableau.
Ainsi pour représenter la matrice
0 1 2
3 4 5
6 7 8
Nous allons pouvoir faire :
@ligne1 = (0, 1, 2);
@ligne2 = (3, 4, 5);
@ligne3 = (6, 7, 8);
@matrice = (\@ligne1, \@ligne2, \@ligne3);
Perl introduit une nouvelle notation pour ce faire : au lieu d'entourer la liste avec des parenthèses, nous allons le faire avec des crochets, ce qui aura pour effet de retourner une référence :
$ligne1_ref = [0, 1, 2];
etc...
ou plus simplement :
@matrice = (
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
);
Dans tous ces cas, @matrice est un tableau de trois éléments, qui sont chacun une référence vers un autre tableau. $matrice[1] est une référence sur la 2ème ligne de la matrice.
Afin d'accéder aux éléments de la matrice, nous pouvons utiliser une nouvelle notation : la flèche '->'.
En effet, afin d'accéder au 3ème élément de $ligne1_ref définie plus haut, nous utiliserons $ligne1_ref->[2] (ce qui est synonyme de ${$ligne1_ref}[2].)
Ainsi, afin d'accéder au '5' dans notre matrice, nous ferons : $matrice[1]->[2].
Mais... Perl est gentil avec nous, car entre deux indices de tableau, la flèche est optionnelle ! Donc nous pouvons écrire : $matrice[1][2], ce qui ressemble maintenant à un tableau bi-dimensionnel.
De même, si $hash_ref est une référence vers un hachage, $hash_ref->{'nom'} renverra l'élément du hachage indexé par 'nom'.
L'ensemble tableaux, hachages et références peut bien sûr être mixé dans l'ordre qui vous plaira. Par exemple, pour réaliser un tableau de personnes, nous écrirons :
@staff = (
[ prenom => "jice", nom => "cardot" ],
[ prenom => "fred", nom => "bonnaud" ],
[ prenom => "serge", nom => "tchesmeli" ],
);
et pour accéder au prénom de la première personne, nous ferons : $staff[0]->{'prenom'}(meuh non je ne suis pas mégalo ;) ou $staff[0]{'prenom'} car ici aussi la flèche est optionnelle.
Notes :
- pour manipuler les tableaux, les fonctions intéressantes sont notamment : push pour ajouter un élément en fin de tableau, pop pour récupérer le dernier élément et shift pour récupérer le premier élément. pop et shift retournent cet élément, et le suppriment du tableau.
- en contexte scalaire, évaluer un tableau ou un hachage retourne son nombre d'éléments. Ainsi $nb_elem = @tableau mettra dans $nb_elem le nombre d'éléments de @tableau.
Les références symboliques
On a vu ci-dessus une utilisation des références, pour le passage et le retour de valeurs dans les fonctions. On vient de voir également leur utilité pour travailler avec des structures de données complexes.
Les références peuvent aussi servir dans un autre contexte : si vous déréférencez une variable contenant une valeur scalaire plutôt qu'un référence vers une autre variable, elle sera considérée comme une référence symbolique, c'est à dire que la valeur scalaire sera utilisée en tant que nom de la variable.
Exemple :
$test = "toto";
$$test = 144; # affecte 144 à $toto
${$test} = 12; # affecte 12 à $toto
$test->[1] = 12; # affecte 12 à $toto[1]
@$test = (1,2,3); # affecte le tableau @toto
Cela peut aller beaucoup plus loin, car vous pouvez mettre une chaîne de caractères entre les accolades :
${$test x 2} = 12; # affecte 12 à $totototo
$titi = 2;
${"$toto$titi"} = 2; # affecte 2 à $toto2
etc.
Remarque : l'utilisation des références symboliques avec use strict; (tel que préconisé au début de l'article) n'est pas possible. Pour contourner ce problème, il faut appeler l'instruction no strict 'refs'; dans le bloc où on veut utiliser les références symboliques.
Autre exemple d'utilisation :
$min = 12;
$max = 144;
while (<FICHIER>) {
if (/^([A-Z]+)\ (\d+)\ (\d+)\ (\d+)$/) {
# les masques ont affecté $1, $2, $3 et $4 (voir [#regex_capture groupement/capture])
print "$1: ";
# on va effectuer une même opération sur $2, $3 et $4 :
for $i (2 .. 4) {
no strict 'refs';
# on utilise les références symboliques : si $i = 2, alors $$i est $2
if ($$i > $max) print "sup ";
elseif ($$i < $min) print "inf ";
else print "ok ";
}
}
}
Manipulation de fichiers
Comme toujours en Perl, il y a plusieurs moyens de faire une même opération.
Ouverture "à la shell" :
Cette façon de faire offrant un moyen simple d'ouvrir un fichier en lecture / écriture est empruntée du shell.
open(FILE, "< nom du fichier"); # ouvre en lecture
open(FILE, " nom du fichier"); # idem
open(FILE, "> nom du fichier'); # ouvre en écriture
open(FILE, ">>nom du fichier'); # ouvre en ajout
Remarque : le fichier "moins" correspond à l'entrée standard stdin en lecture et à la sortie standard stdout en écriture. Par exemple open(SORTIE,">-") ouvre la sortie standard.
Ouverture "à la C" :
La fonction sysopen de Perl correspond à l'appel système open(2) du C. Cela permet une plus grande maîtrise sur l'ouverture des fichiers, au prix d'une plus grande complexité.
Synoptique : sysopen DESCRIPTEUR, CHEMIN, FLAGS, [MASQUE]
MASQUE correspond au masque octal des permissions du fichier (utile pour la création d'un fichier), combiné avec l'umask du processus perl. voir [../admin/permissions.php3 l'article sur les permissions].
Je vous renvoie vers un cours de C pour la signification des exemples suivants, et vous donne la correspondance avec l'open "à la shell".
à la Shell | à la C |
---|---|
open(F, "< $chemin"); | sysopen(F, $chemin, O_RDONLY); |
open(F, "> $chemin"); | O_TRUNC | O_CREAT); |
open(F, ">> $chemin"); | O_APPEND | O_CREAT); |
Bien sûr, sysopen permet de faire plus de choses que le simple open(), en jouant sur les flags ci-dessus et O_RDWR, O_RDONLY, O_EXCL, O_NONBLOCK, O_BINARY. Pour plus d'informations, voir man perlopentut ou man 2 open.
Fermer un fichier
close(FILE);
FILE étant le descripteur du fichier ouvert.
Descripteurs de fichier spéciaux
STDIN et STDOUT sont définis dès le lancement de votre programme. Ils correspondent respectivement à l'entrée et à la sortie standard. Rien ne vous empêche de les redéfinir, en les ouvrant sur un autre fichier, par exemple : open(STDIN, "mon_fichier");
Lire dans un fichier (l'opérateur diamant)
À chaque appel, <DESCRIPTEUR DE FICHIER> (on appelle <> l'opérateur "diamant") retourne la ligne suivante du fichier (en fait jusqu'à la fin de ligne définie par $/, voir le paragraphe sur les [#var_particulieres variables prédéfinies].), ou renvoie faux en fin de fichier.
exemple :
open (F, "mon_fichier");
$premiere_ligne = <F>;
while (<F>) {
... traitement en fonction de $_ ...
}
close(F);
exemple : pour lire tout un fichier dans une variable :
open (F, "mon_fichier");
undef $/;
$contenu_fichier = <F>;
close(F);
La lecture sur l'entrée standard est extrêmement simplifiée, car STDIN est déjà ouvert, et <STDIN> et <> sont synonymes. Ainsi, il suffit de faire :
while (<>) {
... traitement de la ligne en cours ...
}
Écrire dans un fichier
Il suffit de spécifier à la commande print ou printf le descripteur de fichier à utiliser.
Exemple :
open(FLOG, ">> fichier.log");
print FLOG "nouveau log\n";
close(FLOG);
Quelques autres commandes utiles
voir man perlfunc pour la liste exhaustive des commandes intégrées de Perl.
split
split correspond en partie à la fonction cut en shell. Elle permet de découper une chaîne de caractères.
Exemple :
($variable, @tableau) = split (":", "prenom:nom:age:adresse:ville:telephone");
Renvoie "prenom" dans $variable et le reste dans des cases distinctes du tableau @tableau.
systeme ou exec
exec permet de lancer un programme extérieur.
system de même que exec, mais produit un fork, processus fils.
Exemple :
@args = ("ls", "-lrt", "toto*");
system(@args);
Cela va lancer la commande :
ls -lrt toto*
Attention, l'utilisation de ces commandes rend votre script beaucoup moins portable ! En général, il y a moyen de tout faire en Perl, il faut éviter de recourir à ces fonctions.
chomp
chomp supprime tous les caractères de fin de ligne d'une chaîne de caractères et renvoie le nombre de caractères supprimés.
Exemple :
$nb_car = chomp($chaine);
Contrairement à chop qui enlève seulement le dernier caractère de fin de ligne.
Exemple d'un script Perl
Le [perl_script.pl script] va se lire lui-même et sortir à l'écran son contenu modifié :
#!/usr/bin/perl -W
# Fichier script.pl
use strict qw(subs vars refs);
my $file = "$0";
open(FILE, "<$file");
#Lecture du fichier ligne par ligne jusqu'à la fin du fichier.
while (<FILE>) {
#Remplace tous les mots 'file' par 'fichier'.
$_ =~ s/file/fichier/ig;
#Transforme les lettres minuscules de 'a' à 'e' en majuscules.
$_ =~ tr/a-e/A-E/;
#Affiche le résultat des transformations à l'écran.
print $_;
}
close(FILE);
#Fin script.pl
Ce script est totalement inutile mais je crois que cela montre la facilité d'utilisation.
Oui, j'en conviens, en shell cela tiens en une ligne : cat script.pl | sed -e 's/file/fichier/ig' | sed y/a-e/A-E/. Bizarre, cela ressemble au Perl !? ;-)
Obtenir de l'aide ou en savoir plus
Sur notre machine
Et oui ! Sur notre propre machine il y a plein de sources de documentation sans même le savoir.
La première qui viens à l'esprit naturellement c'est LE man.
man perl vous donne quelques généralités, ainsi que la liste de toutes les sections du manuel de perl (perlsyn, perlre, perlvar, perlop, perlfun...).
Ensuite, il y a le man propre à Perl, qui est perldoc. Le meilleur moyen de voir comment cela marche, c'est d'essayer :
perldoc perldoc
Bon, l'inconvénient, c'est que tout ceci est en anglais...
Sur Internet
Sur Internet, nous trouverons plein de sites parlant de Perl. En général, ils parlent des scripts CGI (scripts Perl pour serveur web). Mais néanmoins, il y a
- le site officiel, bien sûr.
- les groupes (français et anglais) marchent bien :
- les sites des mongueurs Perl, suivez mon regard ! ;-)
- Note de Jice : je me dois de citer les deux sites suivants, vu que le regard de Jonesy est sur moi ;)
- les Paris Perl Mongueurs,
- et les Mongueurs de Perl qui fédèrent les groupes locaux (Paris, Lyon, Toulouse...).
- la traduction de la documentation en français.
- une faq sur developpez.com, visiter aussi le forum .
- et enfin, l'intarissable source, Google ! ;-)
Sinon, si on a les moyens les livres O'Reilly sur Perl sont très bien écrits et sont la référence en la matière.
Le mot de la fin
Surtout n'hésitez pas à m'envoyer vos remarques, vos corrections et vos idées, merci.
@ Retour à la rubrique Développement
Copyright
Copyright © 03/04/2002, Jonesy, Jice
Ce document est publié sous licence Creative Commons Attribution, Partage à l'identique, Contexte non commercial 2.0 : http://creativecommons.org/licenses/by-nc-sa/2.0/fr/ |