Guide Perl
Guide Perl
Guide Perl
Sylvain Lhullier
https://formation-perl.fr/
Version 1.4.4
-
mai 2022
Guide Perl https://formation-perl.fr/
Licence 7
Introduction 9
1 Premier pas 11
1.1 Exécuter un programme en Perl . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2 Les types de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3 La notion de contexte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2 Les scalaires 15
2.1 Les délimiteurs de chaı̂nes de caractères . . . . . . . . . . . . . . . . . . . . . . . 15
2.2 Déclaration et utilisation des variables . . . . . . . . . . . . . . . . . . . . . . . . 16
2.3 La valeur undef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.4 Opérateurs, fonctions et contexte numériques . . . . . . . . . . . . . . . . . . . . 17
2.5 Opérateurs, fonctions et contexte de chaı̂nes . . . . . . . . . . . . . . . . . . . . . 17
2.6 Les opérateurs de test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3 Structures de contrôle 21
3.1 Les instructions de test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2 Les boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.3 Un exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4 Listes et tableaux 27
4.1 Valeurs de listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.2 Manipulation de tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.3 Affectations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.4 Multi-déclaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.5 Retour sur l’aplatissement des listes . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.6 Absorption d’une liste par un tableau . . . . . . . . . . . . . . . . . . . . . . . . 31
4.7 La structure de boucle foreach . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.8 Fonctions de manipulation de tableaux . . . . . . . . . . . . . . . . . . . . . . . . 32
4.9 L’opérateur qw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.10 Joindre les éléments dans une chaı̂ne avec join . . . . . . . . . . . . . . . . . . . 34
4.11 Découper une chaı̂ne de caractères en liste avec split . . . . . . . . . . . . . . . 34
4.12 Trier une liste avec sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.13 Sélectionner des éléments avec grep . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.14 Appliquer un traitement à tous les éléments avec map . . . . . . . . . . . . . . . . 36
3
Guide Perl https://formation-perl.fr/
5 Écrire une fonction 37
5.1 Déclaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.2 Appel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.3 Visibilité des variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.4 Une liste pour valeur de retour . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.5 Premier exemple de fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.6 Autre exemple : une fonction récursive . . . . . . . . . . . . . . . . . . . . . . . . 40
5.7 Dernier exemple : le crible d’Ératosthène . . . . . . . . . . . . . . . . . . . . . . . 40
6 Tables de hachage 43
6.1 Déclaration et initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
6.2 Accéder à un élément . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
6.3 Parcours . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.4 Autovivification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.5 Existence et suppression d’une clef . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.6 Tables de hachage et listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
6.7 Exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6.8 Tranches de tableau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.9 Tranches de table de hachage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
7 Manipulation de fichiers 53
7.1 Opérateurs sur les noms de fichier . . . . . . . . . . . . . . . . . . . . . . . . . . 53
7.2 La fonction glob . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
7.3 Premiers exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
7.4 Ouverture de fichier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
7.5 Lecture, écriture et fermeture de fichier . . . . . . . . . . . . . . . . . . . . . . . 56
7.6 Deuxième exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
7.7 Exécution de commandes avec open . . . . . . . . . . . . . . . . . . . . . . . . . 58
8 Expressions régulières 59
8.1 Fonctionnalités . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
8.2 Bind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
8.3 Caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
8.4 Ensembles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
8.5 Quantificateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
8.6 Ensembles (suite) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
8.7 Regroupement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
8.8 Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
8.9 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
8.10 Références arrières . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
8.11 Variables définies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
8.12 Valeurs de retour de m// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
8.13 Exemples de problèmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
8.14 Solutions des problèmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
8.15 Choisir son séparateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
8.16 Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
8.17 Quantificateurs non gourmands . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
9 Références 73
9.1 Références sur scalaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
9.2 Utilisation des références sur scalaire . . . . . . . . . . . . . . . . . . . . . . . . . 74
9.3 Références sur tableau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
9.4 Références sur table de hachage . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
9.5 Réflexions à propos des références . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
9.6 Références anonymes vers scalaire . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
9.7 Références anonymes vers tableau . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
9.8 Références anonymes vers table de hachage . . . . . . . . . . . . . . . . . . . . . 82
9.9 Références anonymes diverses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
9.10 L’opérateur ref . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
9.11 Références circulaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
9.12 Références sur fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
9.13 Références sur fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
9.14 Un dernier mot sur les références . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
10 Les modules 93
10.1 Utilisation d’un premier module . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
10.2 D’autres modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
10.3 Où trouver les modules ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
10.4 Écrire un premier module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
10.5 Et les variables ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
10.6 De la dernière ligne d’un module . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
10.7 Répertoires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
10.8 Blocs BEGIN et END . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
10.9 Introduction à l’export de symboles . . . . . . . . . . . . . . . . . . . . . . . . . . 99
10.10Export par défaut de symboles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
10.11Export individuel de symboles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
10.12Export par tags de symboles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
10.13Exemple complet d’exports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
10.14Fonctions inaccessibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
10.15Documentation des modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
10.16Un dernier mot sur les modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Conclusion 127
L’auteur 129
7
Guide Perl https://formation-perl.fr/
Ce guide Perl sert de support à la formation Perl. C’est une introduction au langage initiale-
ment écrite pour Linux Magazine France et parue dans les numéros de juillet 2002 à février 2003
puis réédité au printemps 2004 dans les Dossiers Linux 2. Depuis, ce document est régulièrement
mis à jour.
Ce langage très riche et puissant est une boı̂te à outils fort utile dans de nombreuses si-
tuations : administration système, manipulation de textes (mail, logs, linguistique, génétique),
programmation réseau (CGI, mod_perl, etc.), bases de données, interfaces graphiques, etc. Ses
nombreuses bibliothèques le rendent vite irremplaçable aux yeux de ceux qui en acquièrent la
maı̂trise. La prise en main du langage est facilitée par de nombreux rapprochements possibles
avec le C, le shell ou awk. Sa conformité POSIX en fait un allié indispensable à l’administrateur
système.
Ce document a la délicate ambition de s’adresser à la fois au programmeur débutant et à
celui qui connaı̂trait bien le C ou le shell. Que le premier me pardonne de faire des comparaisons
avec d’autres langages et de taire peut-être certains points qui me semblent évidents. Que le
second m’excuse de passer à son goût trop de temps à expliquer des notions qui lui semblent
simples ; les choses se corseront au fur et à mesure de la lecture...
Le début du document aborde des notions importantes en Perl et nécessaires pour bien
comprendre la suite. Vous serez sans doute un peu déçu de ne pas faire des choses extrêmement
puissantes immédiatement, mais patience : qui veut aller loin ménage sa monture.
Pour vous mettre en appétit, voici un petit exemple de la concision de Perl et de sa puissance :
Ce programme crée une liste de mots (la phrase de la première ligne), transforme les majus-
cules de ces mots en minuscules, supprime les chiffres appartenant aux mots, supprime les mots
vides et affiche la liste des mots ainsi transformés dans l’ordre lexical. Et dites-vous que vous
aurez en main toutes les notions nécessaires avant la fin de la lecture du document...
Perl est un langage de haut niveau, qui a la prétention de combiner les avantages de plusieurs
autres langages. Première facilité, il gère lui-même la mémoire (ramasse-miettes, pas de limite de
buffers, pas d’allocation à faire, etc.). De plus, les tableaux, les listes et les tables de hachage sont
natifs, ils sont intégrés à la grammaire même du langage. Récursivité, modularité, programmation
objet, accès au système et au réseau, interface avec le C, avec (g)Tk, avec Apache sont aussi au
menu. Et souvenez-vous que l’une des devises de Perl est : there is more than one way to do it
(il y a plus d’une façon de le faire).
9
Guide Perl https://formation-perl.fr/
Premier pas
11
Guide Perl https://formation-perl.fr/
La première ligne est le shebang, bien connu des habitués des scripts en shell. Cette ligne
(qui doit toujours être la première du fichier) indique au système d’exploitation le chemin
de l’exécutable à lancer pour interpréter ce fichier. Le premier caractère doit être un
dièse, ce qui a pour effet que cette ligne est considérée comme un commentaire Perl par
l’interpréteur. Ensuite un point d’exclamation. Puis le chemin absolu vers l’exécutable
perl (à adapter selon votre installation, voir ce que répond type perl ou which perl).
Les deux lignes suivantes sont des pragma qui rendent le langage moins permissif.
Le pragma use strict; permet de rendre le langage moins permissif, notamment en
nous obligeant à déclarer nos variables. Je vous conseille de toujours utiliser ce pragma.
Le pragma use warnings; est positionné dans le but que l’interpréteur affiche des mes-
sages d’avertissement (warnings) à différents propos : il indique les variables utilisées une
seule fois ou utilisées avant d’être initialisées, il signale les redéfinitions de fonctions, etc.
Je vous conseille donc de toujours utiliser ce pragma.
Il faut maintenant rendre ce fichier exécutable et le lancer :
chmod +x salut2.pl
./salut2.pl
Grâce à la ligne de shebang, le système le reconnaı̂t donc comme un programme nécessitant
un interpréteur pour être exécuté. Cet interpréteur est lancé avec pour paramètres les
options fournies dans le shebang ainsi que le nom du fichier.
Cette dernière façon de faire est sans doute la plus courante dès que l’on écrit un pro-
gramme qui sera utilisé plusieurs fois.
Avant de continuer, quelques commentaires sur la syntaxe Perl. Excepté dans les chaı̂nes
de caractères, la présence ou l’absence d’espaces, de sauts de ligne ou de tabulations ne change
pas le comportement du programme, l’indentation est libre. Comme on vient de le voir, une
instruction Perl est terminée par un point-virgule. Les fonctions peuvent prendre leurs arguments
entre parenthèses (comme en C) ; il faut aussi savoir que l’on peut se passer de ces parenthèses.
On aurait pu écrire :
Les deux syntaxes sont tout à fait valides. Dans les cas simples (appel d’une fonction avec
un ou deux paramètres), on pourra omettre les parenthèses. Dans le cas de combinaisons plus
complexes (plusieurs fonctions appelées en paramètre d’autres fonctions, listes en argument...),
je vous conseille de les mettre pour lever toute ambiguı̈té possible.
Un commentaire commence par un dièse (#) et se termine en fin de ligne (comme en shell).
On constitue un bloc d’instructions en les regroupant dans des accolades {} comme en C. Des
exemples suivront.
"Bonjour";
Vous aurez l’occasion de manipuler les contextes tout au long de cette introduction au lan-
gage. Même si cela n’est pas forcément explicite à chaque instant, le contexte est souvent im-
portant.
Les scalaires
Les scalaires sont le type de données atomique de Perl, dit autrement un scalaire est une
donnée atome. Cela signifie que la granularité de données ne va pas au-delà.
Comme dit précédemment, une variable scalaire peut contenir une chaı̂ne de caractères
(String en Java et autres) ou un nombre (entier ou nombre à virgule flottante : int ou float en
C, C++, etc.) ; je ne rentrerai pas dans l’explication de ce ”ou”. Voici des exemples de scalaires
corrects : 12 "texte" ’texte’ -3.14 3e9
Contrairement au C où le caractère \0 de code ASCII 0 (zéro) est le marqueur de fin de
chaı̂ne, en Perl les chaı̂nes de caractères peuvent sans souci contenir ce caractère : "a\0f" est
une chaı̂ne comportant trois caractères. On n’aura donc aucun mal à traiter des fichiers binaires
en Perl.
15
Guide Perl https://formation-perl.fr/
La chaı̂ne ’Bonjour $prenom’ ne comporte pas le contenu d’une hypothétique variable
$prenom mais le caractère dollar suivi de la chaı̂ne prenom.
Puisque les variables ne sont pas substituées, les caractères à protéger sont moins nom-
breux. Le caractère ’ a besoin d’être précédé d’un anti-slash pour apparaı̂tre tel quel
dans une chaı̂ne délimitée par de simples quotes. Il en est de même pour le caractère \ si
celui-ci est suivi d’un autre caractère \
Les nombres n’ont quant à eux pas besoin de délimiteurs pour être manipulés : $x = 10.2
affecte le nombre 10,2 à la variable $x.
my $x;
my $y = 10;
my $z = "hello";
Nous venons ici de déclarer trois variables scalaires. Ces variables seront visibles (accessibles)
dans toute la suite du bloc ainsi que dans les sous-blocs (comme en C) ; comme on s’y attend,
elles ne le seront par contre pas dans les fonctions appelées depuis ces blocs. Le placement des
déclarations est libre dans le bloc (comme en C++), il n’est pas nécessaire de les mettre en
début de bloc.
Voici quelques exemples d’utilisation de variables (on suppose qu’elles sont déjà déclarées) :
$x = $y + 3;
$prenom = "Jules";
$phrase = "Bonjour $prenom";
print("$phrase\n");
Cette dernière ligne affichera à l’écran Bonjour Jules suivi d’un caractère de nouvelle ligne.
Les habitués du shell noteront bien qu’une variable est toujours précédée de son dollar même si
elle est à gauche d’un égal d’affectation.
Structures de contrôle
Ici nous allons apprendre à contrôler le flux des instructions en Perl. En effet un programme
n’est pas qu’une simple suite d’instructions se déroulant linéairement une fois et une seule.
Il faut savoir que Perl (tout comme le C) permet d’indenter notre code comme bon nous
semble, les exemples qui suivent comportent donc des choix personnels d’indentation qui peuvent
diverger des vôtres.
if( $x != 1 ) {
print "$x\n";
}
Ce code Perl a pour effet d’afficher la variable $x si elle ne vaut pas 1. Plusieurs instructions
peuvent être placées dans le bloc, elles seront alors toutes exécutées si la condition est vraie.
Notez que les accolades ({}) sont obligatoires pour délimiter le bloc (contrairement au C).
Il est possible d’exécuter d’autres instructions dans le cas où la condition est fausse. On utilise
pour cela l’opérateur else (sinon en français) qui, lui aussi, est suivi d’un bloc d’instructions :
if( $x == $y ) {
print "\$x et \$y sont égaux\n";
} else {
print "\$x et \$y sont différents\n";
}
Le fait que les accolades sont obligatoires a pour conséquence que le programme suivant est
incorrect :
if( condition1 ) {
instructions1
21
Guide Perl https://formation-perl.fr/
}
else # Attention ce code est incorrect
if( condition2 ) {
instructions2
}
if( condition1 ) {
instructions1
} else {
if( condition2 ) {
instructions2
}
}
Si le programmeur se sent des envies de devenir sylviculteur en plantant des forêts d’ifs, il
faudrait donc qu’il utilise de multiples couples d’accolades. Pour ne pas rencontrer les mêmes
problèmes que le Lisp en rencontre pour les parenthèses ;-), Perl met à notre disposition l’ins-
truction elsif qui permet de cumuler le comportement d’un else et d’un if tout en faisant
l’économie d’un couple d’accolades :
if( condition1 ) {
instructions1
} elsif( condition2 ) {
instructions2
} else {
instructions3
}
L’instruction switch de C n’a pas d’équivalent direct en Perl ; il faut pour cela planter une
forêt d’ifs, comme dans l’exemple précédent.
Mais Perl n’en reste pas là. Il existe une syntaxe très utilisée pour effectuer une unique
instruction si une condition est vérifiée :
On parle ici de modificateur d’instruction. Pour cette syntaxe, les parenthèses sont option-
nelles autour de la condition, mais je vous conseille de les mettre systématiquement pour une
meilleure lisibilité. Le code suivant affiche la variable $s si elle est définie :
La boucle for prend trois expressions entre parenthèses : la première expression permet
d’initialiser la variable de boucle, la deuxième est une condition de continuation et la dernière
permet de modifier la valeur de la variable de boucle.
Quand la boucle démarre, la variable est initialisée (expression 1) et le test est effectué
(expression 2). Si cette condition est vérifiée, le bloc d’instructions est exécuté. Quand le bloc
se termine, la variable est modifiée (expression 3) et le test est de nouveau effectué (expression
2). Si la condition est vérifiée, le bloc d’instructions est réexécuté avec la nouvelle valeur pour
la variable de boucle.
Tant que le test reste vrai, le bloc d’instructions et l’expression de modification de la variable
sont exécutés. À l’arrêt de la boucle, les instructions qui suivent la boucle sont exécutées.
L’exemple suivant affiche tous les entiers pairs de 0 à 20 inclus :
La boucle s’arrête lorsque $i vaut 22. Cette variable est déclarée dans le bloc d’initialisation
et n’existe donc que dans la boucle. Notez qu’il est tout à fait possible d’utiliser une variable pré-
existante comme variable de boucle (et donc de ne pas faire de my dans la partie initialisation) ;
dans ce cas, après exécution de la boucle, la variable vaut la dernière valeur qui lui a été affectée
au cours de la boucle.
Une autre boucle existe : la boucle while (tant que en français) dont voici la syntaxe :
while( condition ) {
instructions;
}
Les instructions sont effectuées tant que la condition est vraie. La partie initialisation doit
avoir été effectuée avant la boucle ; la partie modification de la variable doit avoir lieu dans le
bloc d’instructions.
L’exemple suivant affiche lui aussi les entiers pairs de 0 à 20 :
my $i = 0;
while( $i <= 20 ) {
print "$i\n";
$i+=2;
}
3.3 Un exemple
Voici un petit exemple de programme Perl ; il n’est pas très utile dans la vie de tous les jours,
mais il utilise beaucoup des notions abordées jusqu’ici. Si vous parvenez à comprendre tout ce
qu’il fait, vous n’aurez pas perdu votre temps à le lire !
9 impair
---------
7 impair
------
5 impair
3 impair
---
1 impair
#1#2#3#4#5#6#7#8#9#
Listes et tableaux
Les scalaires et les expressions de base n’ont maintenant plus aucun secret pour vous. Des
notions plus complexes et des fonctions plus puissantes sont alors à notre portée. C’est le cas des
listes, des tableaux et de l’impressionnant arsenal de fonctions Perl permettant de les manipuler ;
vous verrez tout ce qu’il est possible de faire en Perl avec ces deux concepts a priori anodins.
Une liste est une suite (donc ordonnée) de valeurs scalaires. Nous verrons comment créer une
liste, la manipuler, la parcourir, etc.
Une variable de type tableau peut contenir plusieurs valeurs scalaires. Cette notion est
présente dans de nombreux langages de programmation et ne posera sans doute problème à
personne.
Les passerelles entre listes et tableaux sont nombreuses et très intuitives en Perl. C’est pour
cela que nous n’entrerons pas ici dans les détails de la distinction entre liste et tableau. Dans ce
document, j’utiliserai chacun des deux termes à bon escient sans forcement indiquer explicitement
pourquoi j’utilise l’un plutôt que l’autre, mais les notions pourront apparaı̂tre naturelles au
lecteur sans qu’il ne soit nécessaire de préciser les choses formellement.
27
Guide Perl https://formation-perl.fr/
et on aurait obtenu la même liste. On appelle cela l’aplatissement (ou la linéarisation) des listes.
Pour constituer une liste de listes, il faudra faire usage de références (notion que nous aborderons
plus tard).
L’opérateur de répétition (x), que l’on a déjà appliqué aux chaı̂nes de caractères précédemment,
s’applique aussi aux listes : (2,10) x 3 est une liste à six éléments valant (2,10,2,10,2,10).
my @t = ();
Pour lui donner une valeur lors de sa déclaration, il faut faire ainsi :
my @t = (3,’chaine’,"bonjour $prenom");
Il est intéressant de savoir qu’il est possible d’accéder au dernier élément d’un tableau en
utilisant l’indice -1 : $t[-1] est le dernier élément de @t. De la même façon, $t[-2] est l’avant-
dernier, etc.
Il est possible de connaı̂tre l’indice du dernier élément d’un tableau @t grâce à la variable $#t
On a donc $t[$#t]. équivalent à $t[-1] (ce dernier étant bien plus lisible). Il peut être utile de
savoir que l’expression scalar(@t) (c’est-à-dire l’utilisation d’un tableau en contexte scalaire)
donne le nombre d’éléments du tableau @t (ce qui vaut 1 de plus que $#t) ; $x=@t donnerait la
même chose.
Il faut savoir que vous ne générerez pas d’erreur (débordement ou autre) si vous tentez
d’accéder à un élément au-delà du dernier. La valeur de cet élément sera simplement undef et le
programme continuera. Depuis la version 5.6 de Perl, l’instruction exists (que l’on retrouvera
pour les tables de hachage) permet de tester l’existence d’un élément d’un tableau :
Car on teste ici si l’expression $t[100] vaut undef ou non, ce qui peut être vrai dans deux
cas : soit l’élément existe et vaut undef, soit l’élément n’existe pas...
Voici une autre illustration du fait que vous n’avez pas à vous soucier de problèmes d’allo-
cation mémoire :
my @t = (3,23.4,"as");
$t[1000] = 8;
Ces trois programmes sont lancés avec les trois mêmes arguments. Sachez que, contrairement
au langage C, le nom du programme n’est pas contenu dans @ARGV qui ne comporte donc que les
arguments au sens strict. La variable spéciale $0 (comme en shell) contient le nom du programme
(nul besoin de déclarer cette variable pour l’utiliser).
4.3 Affectations
Il est possible d’affecter un tableau à un autre tableau en une seule instruction :
@t = @s;
Cette instruction copie le tableau @s dans le tableau @t. Le tableau @t perd ses anciennes
valeurs, prend celles de @s et sa taille devient celle de @s : on obtient bien deux tableaux tout
à fait identiques (et distincts, la modification de l’un n’entraı̂nant nullement la modification de
l’autre).
Voici d’autres instructions d’affectation mêlant tableaux et listes :
• ($a,$b) = (1,2); Cette instruction affecte une valeur à chacune des variables de la liste
de gauche : $a reçoit 1 et $b reçoit 2 ;
• ($a,$b) = (1,2,3); Les mêmes affectations sont effectuées ici, la valeur 3 n’étant d’au-
cune utilité ;
4.4 Multi-déclaration
Pour déclarer plusieurs variables avec un seul my, le débutant aurait tendance à écrire la
chose suivante (il n’y a pas de honte !) :
my $a,$b; # Incorrect !
Ceci est incorrect. Pour pouvoir faire cela, il nous faut utiliser une liste :
my ($a,$b);
Les variables $a et $b sont créées et valent undef. Pour leur affecter des valeurs, il faut là
aussi utiliser une liste (ou un tableau) :
my ($a,$b) = (1,2);
my ($c,$d) = @t;
@t = (1,2,"age");
@t2 = (10,@t,20);
Le tableau @t2 ne comporte pas trois éléments dont celui du milieu serait lui-même un
tableau, mais contient les cinq éléments, résultat de l’aplatissement du tableau @t dans la liste
de droite lors de l’affectation de @t2. Cette affectation a eu le même résultat qu’aurait eu la
suivante :
@t2 = (10,1,2,"age",20);
($a,@t) = @s;
Le membre gauche de l’affectation est constitué d’une liste comportant une variable scalaire
et un tableau. Il n’y a pas à proprement parler d’aplatissement de liste, car il s’agit ici d’une
l-value (membre gauche d’une affectation), mais la variable $a reçoit le premier élément du
tableau @s et le tableau @t absorbe tous les autres (@s n’étant bien sûr pas modifié).
En fait dans cette syntaxe, le premier tableau rencontré dans la liste de gauche reçoit tous les
éléments restant de la liste de droite. D’éventuelles autres variables qui le suivraient (cas idiot,
mais bon...) seraient mises à undef s’il s’agit de scalaires et à vide s’il s’agit de tableaux. Par
exemple, l’affectation suivante :
@s = (10,1,2,"age",20);
($a, @t, @u, $b) = @s;
équivaut à :
@s = (10,1,2,"age",20);
$a = 10;
@t = (1,2,"age",20);
@u = ();
$b = undef;
Simple et intuitif.
foreach $v (1,43,"toto") {
print "$v\n";
}
Ce petit programme affiche chaque élément de la liste sur une ligne. Ces autres exemples
sont valides eux aussi :
foreach my $v (@t) {
print "$v\n";
}
Il est aussi possible de ne pas utiliser explicitement de variable de boucle ; dans ce cas c’est
la variable spéciale $_ qui sera automatiquement utilisée :
foreach (@t) {
print "$_\n";
}
Comme pour les autres boucles, l’instruction next passe à la valeur suivante sans exécuter
les instructions qui la suivent dans le bloc. L’instruction last met fin à la boucle.
Voici un petit exemple d’utilisation de foreach affichant des tables de multiplication :
#!/usr/bin/perl
use strict;
use warnings;
die("Usage: $0 <n> <n>\n")
if( !defined( $ARGV[1] ) );
foreach my $i (1..$ARGV[0]) {
foreach my $j (1..$ARGV[1]) {
printf( "%4d", $i*$j );
}
print "\n";
}
Et le voici à l’œuvre :
./mult.pl
Usage: ./mult.pl <n> <n>
./mult.pl 5 3
1 2 3
2 4 6
3 6 9
4 8 12
5 10 15
Passons à la suite.
4.9 L’opérateur qw
L’opérateur qw nous permet de créer facilement une liste de chaı̂nes de caractères. En effet,
il peut sembler pénible de constituer une longue liste de tels éléments en raison du fait qu’il faut
délimiter chacun d’entre eux au moyen de simples ou de doubles-quotes :
La chaı̂ne de caractères sera découpée selon les espaces, les tabulations et les retours à la
ligne.
Les délimiteurs les plus souvent utilisés sont les parenthèses (comme dans l’exemple précé-
dent) ainsi que les slashs :
Cette fonction est bien pratique, mais peut être source d’erreurs, voyez l’exemple suivant :
Les simples quotes (’) semblent indiquer que le programmeur souhaite constituer un seul
élément comportant les mots aux et erreurs ; ce n’est pas ce qui est fait ici. En effet, ni les
simples quotes ni les doubles-quotes ne constituent un moyen de regrouper des mots pour l’opé-
rateur qw. La liste ainsi créée comporte donc quatre éléments ; on aurait pu écrire :
("attention","’aux","erreurs’","b^ etes").
Une fonction est un ensemble d’instructions regroupées de manière à être utilisées plusieurs
fois sans avoir à dupliquer du code.
5.1 Déclaration
Le mot clef sub permet de définir des fonctions en Perl. Les arguments d’une fonction sont
des valeurs scalaires, à l’exclusion de toutes autres (on verra comment faire en sorte de passer un
tableau en argument) ; ces paramètres sont accessibles via la variable spéciale @_ (qui est donc
un tableau). Modifier une valeur de @_ modifiera les variables d’appel, il est donc d’usage d’en
faire une copie avant manipulation.
sub maJolieFonction {
my ($x,$y,$t) = @_;
... instructions ...
return $z;
}
Ces quelques lignes définissent une nouvelle fonction dont le nom est maJolieFonction.
Cette fonction copie dans trois variables locales les trois premières valeurs du tableau @_, c’est-
à-dire ses trois premiers paramètres (les règles classiques d’affectation entre listes et tableaux
s’appliquent ici). Je vous conseille de toujours commencer vos fonctions par une ligne copiant
les valeurs de @_ et de ne plus utiliser @_ dans la suite de la fonction (sauf cas spécial). Si votre
fonction attend un seul paramètre, la syntaxe peut être la suivante :
my ($x) = @_;
my $x = @_; #incorrect
Cette ligne est incorrecte, car dans ce cas, la variable $x aurait pour valeur le nombre de
paramètres (affectation d’un tableau à un scalaire). La syntaxe suivante peut aussi être utile :
my ($x,@t) = @_;
37
Guide Perl https://formation-perl.fr/
la variable $x reçoit le premier paramètre et le tableau @t reçoit tous les paramètres restants.
Enfin, une autre écriture que vous verrez souvent dans les programmes Perl est la suivante :
my $x = shift;
celle-ci s’appuie sur le fait que dans une sous-routine, la fonction shift travaille par défaut
sur @_.
L’instruction return met fin à l’exécution de la fonction et on peut lui fournir une expression
qui sera alors la valeur de retour de la fonction.
5.2 Appel
La fonction ainsi définie peut être appelée au moyen de la syntaxe suivante :
maJolieFonction(10,20,30);
Dans ce cas, l’éventuelle valeur de retour est ignorée. Pour récupérer cette valeur :
$v = maJolieFonction(10,20,30);
mais cela peut créer des ambiguı̈tés et je vous déconseille donc cette syntaxe.
S’il est possible en Perl d’imposer le nombre d’arguments pour une fonction (nous n’en
parlerons pas ici), cela n’est pas fait par défaut. Rien ne nous empêche en effet d’appeler la
fonction maJolieFonction précédemment définie avec deux ou quatre arguments, alors qu’elle
semble en attendre trois ; si on l’appelle avec deux arguments, la variable $t vaudra undef ; par
contre si on l’appelle avec plus de trois arguments, les valeurs suivantes seront ignorées. Mais
cette particularité du langage est parfois bien pratique, notamment pour écrire des fonctions à
nombre variable d’arguments.
my $a = 3;
my $b = 8;
my $c = 12;
sub maJolieFonction {
my $a = 5;
print "$a\n"; # affiche 5
De manière plus générale, les variables déclarées au moyen de my sont visibles jusqu’à la fin
du plus petit bloc qui les englobe. En particulier, dans une fonction...
sub maJolieFonction2 {
my $d = -3;
if( ... ) {
my $d = 4;
my $e = 8;
print "$d\n"; # affiche 4
print "$e\n"; # affiche 8
}
print "$d\n"; # affiche -3
print "$e\n"; # $e n’existe pas ici
}
return ($x,$z);
return @t;
Dans le second cas, le tableau est converti en liste. Et voici comment il est possible de
récupérer ces valeurs :
@s = fonction(...);
($j,$k) = fonction(...);
Ces deux manières de procéder peuvent parfaitement être utilisées chacune dans les deux cas
de return pré-cités (ce sont toujours les mêmes règles d’affectation qui s’appliquent).
Un bon programme Perl commence toujours par les première et deuxième lignes. Si la variable
scalaire $t, elle, est globale, en revanche les variables $x, $z et $m sont locales à la fonction. En
ligne 12, le tableau @t reçoit pour valeur la liste renvoyée par la fonction. Notez bien qu’il n’y
a aucun conflit entre les variables $t et @t ; en effet, l’instruction de la dernière ligne procède
d’abord à l’affichage de la variable scalaire $t puis du premier et deuxième éléments du tableau
@t (les crochets permettent de savoir qu’il s’agit d’éléments d’un tableau).
sub Fact {
my ($n) = @_;
return 1
if( $n == 1 || $n == 0 );
return $n * Fact($n-1);
}
print Fact(5)."\n"; # affiche 120
sub Crible {
my ($n) = @_;
# Liste initiale :
my @nombres = (2..$n);
# Liste des nombres premiers trouvés :
my @premiers = ();
Quiconque a déjà réalisé cet algorithme en C ou C++ comprendra la joie que cette concision
procure...
Tables de hachage
Les tables de hachage de Perl ne se retrouvent pas dans beaucoup d’autres langages ; pour
les avoir souvent utilisées en Perl, il est dur de repasser à des langages qui n’en sont pas pourvus.
Une table de hachage (hash table en anglais) est un type de donnée en Perl permettant
d’associer une valeur à une clef. On peut dire d’un tableau (notion abordée précédemment)
qu’il associe une valeur scalaire à un entier : à la position i (pour i entier), une certaine valeur
scalaire est présente. Une table de hachage va nous permettre d’aller au-delà : on pourra faire
correspondre une valeur scalaire (comme pour un tableau) à toute chaı̂ne de caractères (plutôt
qu’à un entier).
Je peux, par exemple, avoir envie de gérer en Perl un index téléphonique simple : chacun
de mes amis a un numéro de téléphone, je veux pouvoir retrouver leur numéro à partir de leur
prénom. Je vais donc associer le numéro au prénom :
Les prénoms seront les clefs, c’est-à-dire le ”point d’entrée” dans la table de hachage (comme
les indices numéraux le sont pour les tableaux). Les numéros de téléphone seront les valeurs
associées à ces clefs. Il s’agit bien d’une association chaı̂ne de caractères vers scalaire.
Vous l’avez sans doute compris, dans une table de hachage, une clef n’est présente qu’une
seule fois et ne peut donc avoir qu’une seule valeur (comme l’élément d’un indice donné d’un
tableau). Par contre, une valeur peut être associée à plusieurs clefs.
my %h;
On a alors une table de hachage vide (aucune clef). Il est possible de signaler explicitement
que l’on déclare une table de hachage vide :
my %h = ();
43
Guide Perl https://formation-perl.fr/
Pour donner des valeurs initiales à notre table de hachage, on peut utiliser la syntaxe sui-
vante :
Cette dernière table de hachage est déclarée et initialisée avec les clefs Paul, Virginie et
Pierre ayant respectivement pour valeurs
01.23.45.67.89, 06.06.06.06.06 et heu ...
$h{Jacques} = "02.02.02.02.02";
print "Tél : $h{Jacques}\n";
$h{’Jean-Paul’} = "03.03.03.03.03";
if( $h{"Jean-Paul"} ne "Heu ..." ) {
...
}
La clef utilisée pour cette syntaxe peut tout à fait être contenue dans une variable scalaire
(qui sera évaluée en contexte de chaı̂ne de caractères) :
my $k = "Jacques";
$h{$k} = "02.02.02.02.02";
$h{hello} .= "après";
associe à la clef hello la valeur chaı̂ne vide puis lui concatène la chaı̂ne "après". De la même
façon, l’expression
$h{bye}++;
Dans la suite nous verrons comment découper un texte en mots au moyen des expressions
régulières.
Il est important de noter qu’un test effectué au moyen de l’opérateur defined aurait été
possible, mais dangereux. En effet, l’expression defined( $h{hello} ) est fausse dans deux cas
très différents : soit si l’élément n’existe pas, soit si l’élément existe et vaut undef ; elle sera vraie
si l’élément existe et ne vaut pas undef. Il est donc impossible de distinguer le cas d’un élément
absent et celui d’un élément indéfini (valant undef) avec defined.
Cette distinction entre absent et indéfini peut paraı̂tre artificielle dans ce cas (elle peut tout
de même être importante dans certaines situations !), mais dans le cas de la suppression d’une
clef, il en est tout autrement.
Pour supprimer une clef dans une table de hachage, il faut utiliser l’opérateur delete. L’ins-
truction
delete( $h{hello} );
supprime la clef hello de la table %h si elle existe (si elle n’existe pas, elle ne fait rien). De
la même façon que exists est la bonne méthode pour tester l’existence d’un élément, delete
est la bonne méthode pour en supprimer un. Le débutant pourrait être tenté d’écrire :
Ce qui est fort différent, car dans ce cas, la clef hello aura une valeur indéfinie, mais existera
toujours ! On la retrouvera, par exemple, dans les parcours effectués au moyen des opérateurs
keys, values ou each ; ce qui n’est sans doute pas le but recherché.
Pour résumer, on peut dire que pour tester l’existence d’une clef, il faut utiliser exists et
que pour en supprimer une, il faut utiliser delete.
En marge de ces deux fonctions, voici une manière de savoir si une table de hachage est vide
ou non (on qualifie de vide une table de hachage qui ne comporte aucune clef). Cette syntaxe
utilise la table de hachage en contexte de chaı̂ne de caractères, par exemple de cette façon :
if( %h == 0 ) {
print "%h est vide\n";
}
La valeur d’un hachage en contexte scalaire, comme dans l’exemple précédent ou dans l’ex-
pression scalar(%h) est le nombre de clefs présentes dans la table hachage (ou nombre de
valeurs, ce qui revient au même).
Jusqu’à la version 5.26 de Perl, la valeur d’un hachage en contexte scalaire était du type
4/8 qui indiquait le nombre de places (buckets en anglais) utilisées par rapport au nombre total
disponible dans le hachage. Une table vide était un cas particulier, elle renvoyait 0.
La première instruction crée un tableau @t initialisé à une liste à six éléments. La seconde
crée une table de hachage %h initialisée au moyen du précédent tableau. Les valeurs du tableau
sont prises deux à deux : la première de chaque couple sera la clef dans la table de hachage, la
seconde la valeur. Si le nombre d’éléments de la liste est impair, la dernière clef créée aura undef
pour valeur. Si une clef venait à être présente plusieurs fois dans la liste, c’est la dernière valeur
qui sera prise en compte dans la table de hachage.
On aurait aussi pu écrire :
Il est à noter que cette syntaxe rappelle étrangement l’un des premiers exemples de création
de table de hachage qui utilisait => pour séparer clefs et valeurs. Cette similarité est en fait
une quasi-équivalence, car l’opérateur => peut être utilisé à la place de la virgule pour créer des
listes ; il n’a été ajouté au langage Perl que pour faciliter la lecture des affectations de tables
de hachage, car il force un contexte de chaı̂ne à sa gauche, ce qui permet justement d’écrire
%a = ( toto => ’titi’ );
La conversion dans l’autre sens est aussi possible. L’évaluation d’une table de hachage dans
un contexte de liste renvoie une liste des clefs et des valeurs, se suivant respectivement deux à
deux, dans un ordre quelconque entre couples. La table de hachage %h de l’exemple précédent
peut être affectée à un tableau :
my @t2 = %h;
foreach my $x (%h) {
print "$x\n";
}
La fonction reverse, qui nous a permis d’inverser les listes, peut être employée pour inverser
une table de hachage :
%h = reverse(%h);
Les valeurs deviennent les clefs et inversement. Si plusieurs valeurs identiques sont présentes,
le comportement est imprévisible, car certes, lors de la transformation de liste en table de hachage
la dernière valeur compte, mais lors de la transformation de table de hachage en liste l’ordre est
quelconque...
L’association individu - numéro de téléphone est idéale pour illustrer cela :
On pourra alors retrouver la personne à partir de son numéro de téléphone. Si, par contre,
Paul et Virginie avaient eu le même numéro, on n’aurait pas pu prédire quelle serait la personne
renvoyée.
6.7 Exemples
Voici quelques exemples d’utilisation des tables de hachage.
Le premier concerne la variable spéciale %ENV qui contient les variables d’environnement du
programme. $ENV{PATH} contient le path, $ENV{HOME} vaut le nom du répertoire personnel de
l’utilisateur qui exécute le programme, etc.
Deuxième exemple, les tables de hachage peuvent servir à constituer des tableaux à plu-
sieurs dimensions ; on pourrait en effet imaginer avoir des clefs qui seraient la concaténation des
coordonnées dans les n dimensions : dim1 :dim2 :dim3 :...
my %h = ();
foreach my $i (0..4) {
foreach my $j (-3..10) {
foreach my $k (130..148) {
$h{"$i:$j:$k"} = Calcul($i,$j,$k);
}
}
}
Nous verrons dans la suite qu’il est possible de bâtir de réels tableaux à plusieurs dimensions
en utilisant des références.
L’exemple suivant concerne les ensembles ; nous allons utiliser les tables de hachage pour
calculer l’union et l’intersection de deux ensembles.
print("@union\n");
# affiche : 1 2 3 5 6 7 8 9
print("@inter\n");
# affiche : 3 5 7
Pour le même problème, voici une solution n’utilisant qu’une seule table de hachage, je vous
laisse le soin d’en apprécier le principe :
my @ensA = (1, 3, 5, 6, 7, 8);
my @ensB = (2, 3, 5, 7, 9);
print("@union\n");
# affiche : 1 2 3 5 6 7 8 9
print("@inter\n");
# affiche : 3 5 7
La compréhension de cet exemple demande d’avoir assimilé plusieurs notions importantes
vues jusqu’ici.
@t[4,10] = (4321,"age");
cette instruction affecte 4321 à l’indice 4 du tableau @t et la chaı̂ne age à l’indice 10. On
aurait pu écrire
($t[4],$t[10]) = (4321,"age");
Une autre utilisation des tranches de tableau apparaı̂t avec les fonctions qui renvoient une
liste. Par exemple la fonction stat prend en paramètre un nom de fichier et renvoie toutes sortes
d’informations sur le fichier : taille, dates, propriétaire etc. Il est courant d’écrire :
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($filename);
La fonction renvoie une liste qui est affectée aux variables de la liste de gauche. Les tranches
peuvent intervenir si seules quelques informations vous intéressent et que vous ne voulez pas
déclarer de variables inutiles. Par exemple, si seules les dates de modification (indice 9) et de
création (indice 10) vous intéressent, vous pouvez écrire :
L’appel à la fonction est placé entre parenthèses et on ne prend que les éléments d’indice 9
et 10 de sa valeur de retour. On a alors une liste à deux éléments, celle-ci est affectée à la liste
à gauche du signe égal et donc ces deux éléments sont affectés aux deux variables.
Le tableau @t comporte alors une fois et une seule chacun de ses éléments.
Manipulation de fichiers
Pour le moment nous avons écrit des programmes dont les interactions avec leur environne-
ment étaient faibles. Nous allons voir dans cette partie comment manipuler des fichiers en Perl.
Les fichiers se retrouvent dans tous les langages, mais la manière très simple et très puissante
de les manipuler fait des fichiers une facilité de Perl.
my $file = "/usr/doc/perl";
53
Guide Perl https://formation-perl.fr/
if( -f $file && -w $file ) { .... }
my $taille = -s $file;
my $age = -M $file;
Simple et efficace.
if( ! open($fd,’>>’,’data.txt’) ) {
exit(1);
}
Dans ce premier exemple, on tente d’ouvrir en ajout un fichier data.txt ; en cas d’impossi-
bilité, on appelle la fonction exit qui met fin au programme (la valeur 1 signale une erreur ; la
valeur 0 signalant la fin normale du programme, les autres valeurs sont utilisées pour signaler
au shell appelant une erreur). Vous noterez que l’utilisateur qui exécute le programme n’est pas
informé de la cause de l’échec ; le programme se termine, tout au plus sait-il qu’il y a eu un
problème avec l’ouverture de ce fichier, mais il n’en connaı̂t pas la cause. L’exemple suivant va
nous permettre de lui afficher un joli message d’erreur :
Nous cherchons ici à ouvrir en lecture le fichier dont le nom serait la concaténation de la
chaı̂ne /tmp/ et du contenu de la variable $a. La fonction die met fin au programme comme
exit le ferait, mais affiche en plus le paramètre qu’on lui passe. En l’occurrence, le paramètre
fourni est la chaı̂ne de caractères comportant le nom de la fonction qui cause l’échec (on aurait
pu ajouter le nom du fichier) ainsi que la variable $!. En contexte de chaı̂ne de caractères,
cette variable magique $! contient le message errno de la dernière erreur survenue, par exemple
$l = <$fd>;
Cette instruction lit la prochaine ligne disponible du fichier FIC. Vous noterez bien que l’opé-
rateur chevrons (diamond operator en anglais) est ici en contexte scalaire. En contexte de liste,
il renvoie la liste de toutes les lignes restant dans le fichier :
@t = <$fd>;
Cette instruction ≪ absorbe ≫ toutes les lignes du fichier dans une liste qui est placée dans
le tableau @t.
Pour itérer sur les lignes d’un fichier, il est courant de faire ainsi :
La boucle while donne pour valeur à la variable $l une à une toutes les lignes du fichier.
La fonction chomp supprime le dernier caractère s’il s’agit d’un retour à la ligne. La variable
spéciale $. vaut le numéro de la ligne courante du dernier fichier lu (ici $fd).
Si vous utilisez la variable spéciale omniprésente $_, la construction
while( <$fd> )
Pour écrire dans un fichier, nous allons utiliser les fonctions print et printf que nous avons
déjà vues. Elles prennent en premier argument le descripteur de fichier :
Il faut noter qu’il n’y a pas de virgule après le descripteur de fichier (il ne faut pas en mettre !).
Les parenthèses sont comme toujours optionnelles autour des arguments, mais permettent de
lever certaines ambiguı̈tés. La fonction printf fonctionne comme printf ou fprintf du C et
ne sera donc pas détaillée ici (voir man~3~printf).
Pour fermer un descripteur de fichier (et donc vider les buffers associés), il faut faire appel
à la fonction close :
close( $fd );
À noter que si vous réutilisez un descripteur de fichier dans un open sans faire de close au
préalable, Perl ne rouspétera pas et fermera consciencieusement le premier fichier avant d’ouvrir
le deuxième.
Il existe plusieurs fichiers ouverts automatiquement par Perl dès le lancement du programme :
• STDIN : l’entrée standard (souvent le clavier) ;
• STDOUT : la sortie standard (souvent le terminal). Par défaut print et printf écrivent
sur ce flux ;
• STDERR : la sortie d’erreur standard (souvent le terminal). Par défaut warn et die écrivent
sur ce flux ;
• ARGV : ce descripteur est un peu spécial, mais souvent bien pratique. Les lignes lues sont
celles des fichiers de la ligne de commande (donc les arguments passés au programme sont
considérés comme des noms de fichier) ; si le programme est lancé sans argument, l’entrée
standard est lue. N.B. Vous pouvez écrire soit <ARGV> soit <> La variable spéciale $ARGV
contient le nom du fichier en cours de lecture.
Discutons un peu de la manipulation de fichiers binaires. Les exemples de lecture de fichiers
donnés jusqu’ici ne conviennent pas à de tels fichiers, mais plutôt à des fichiers contenant du texte.
Vous pouvez, pour cela, utiliser la fonction getc qui renvoie le prochain caractère disponible :
$c = getc($fd);. Vous pouvez aussi faire usage de la fonction read qui lit un nombre déterminé
de caractères : $tailleLue = read(~$fd,~$tampon,~$tailleÀLire );. Les données seront
placées dans la variable $tampon. À la fin du fichier, ou s’il y a un problème, le tampon n’est
pas complètement rempli. C’est pour cela que l’on récupère la valeur de retour de read.
Pour écrire des données non textuelles dans un fichier, vous pouvez tout à fait utiliser les
fonctions print et printf, car les chaı̂nes de caractères de Perl peuvent contenir le caractère de
code ASCII zéro. On notera que la fonction write existe, mais n’est pas l’inverse de read.
S’il vous est nécessaire d’utiliser les fonctions d’entrée/sortie bas niveau, voici leurs noms en
Perl : sysopen, sysread, syswrite et close.
#!/usr/bin/perl
use strict;
use warnings;
On effectue une boucle while sur les lignes du fichier. Chaque ligne est alors découpée en
mots par la fonction split (\W+ correspond aux suites de caractères non alphanumériques, nous
verrons cela dans la suite lorsque nous étudierons les expressions régulières). Chaque mot est mis
en minuscules au moyen de l’opérateur tr (que nous expliquerons avec les expressions régulières).
open(my $fd1,"ls|")
open(my $fd2,"df -HT $device|")
Les lignes lues via le descripteur de fichier ainsi créé seront celles que la commande aurait
affichées à l’écran si on l’avait lancée depuis un terminal.
La syntaxe open(HANDLE,"|commande") permet de lancer une commande. Les lignes écrites
dans le descripteur de fichier constitueront son entrée standard, par exemple :
Expressions régulières
Nous abordons ici un sujet très riche en développements : les expressions régulières. Perl en
tire une partie de sa grande puissance pour l’analyse et le traitement des données textuelles.
La lecture de cette partie du document peut aussi intéresser toute personne utilisant grep, sed,
Python, PHP, C, C++ et même Java.
Atout important de Perl par rapport à d’autres langages, les expressions régulières permettent
de manipuler le texte de façon très puissante et très concise. L’acquisition de leur maı̂trise peut
s’avérer difficile au début, mais en vaut très largement la chandelle, aussi bien pour programmer
en Perl que pour utiliser les outils classiques du shell ou les autres langages précédemment cités.
Au niveau vocabulaire, on utilise en anglais le terme regular expression (souvent abrégé en
regexp, voire regex), ce qui a donné en français une traduction correcte ”expressions rationnelles”
et une traduction mot à mot ”expressions régulières”. La seconde est entrée dans les mœurs et
sera donc utilisée ici.
On retrouve les expressions régulières dans certaines fonctions Perl que vous connaissez déjà,
comme split ou grep ; mais elles existent aussi par le biais d’opérateurs spécifiques.
8.1 Fonctionnalités
Il existe deux types principaux de fonctionnalités dans les expressions régulières : la corres-
pondance (pattern matching en anglais : pattern=motif, matching=correspondance) et la substi-
tution.
La correspondance est le fait de tester (vérifier) si une chaı̂ne de caractères comporte un
certain motif. Par exemple, on pourrait se poser les questions suivantes et y répondre par un
match : la variable $v commence-t-elle par un chiffre ? Comporte-t-elle au moins deux lettres
majuscules ? Contient-elle une sous-chaı̂ne répétée deux fois d’au moins cinq caractères ? Etc.
Sa syntaxe est la suivante : m/motif/
Le m indique que nous voulons faire un match, les slashes (/) servent à délimiter le motif
recherché (on verra plus loin comment utiliser d’autres séparateurs).
Cette fonctionnalité nous permettra aussi d’extraire des sous-chaı̂nes d’une variable donnée
sans la modifier. Par exemple, si je veux récupérer le premier nombre que comporte $v, j’utiliserai
aussi la correspondance.
La substitution permet de faire subir des transformations à la valeur d’une variable. Par
exemple : remplacer dans la variable $v toutes les sous-chaı̂nes toto par titi. Supprimer de $v
tous les mots entre guillemets. Etc.
59
Guide Perl https://formation-perl.fr/
Sa syntaxe est la suivante : s/motif/chaı̂ne/
Le s indique que nous voulons faire une substitution, les slashes (/) servent à délimiter le
motif recherché ainsi que la chaı̂ne de remplacement.
8.2 Bind
Pour ”lier” une variable à une telle expression, il faut utiliser l’opérateur =~ (dit bind en
anglais).
$v =~ m/sentier/ vérifie si la variable $v comporte le mot sentier. On dit alors que la
variable est ”liée” à l’expression régulière. Cette expression vaut vrai ou faux ; nous l’utiliserons
donc très souvent dans une structure de contrôle de type if :
if( $v =~ m/sentier/ ) {
instructions
}
Dans les cas où le test est vrai, c’est-à-dire si la variable contient le motif (ici si $v contient
sentier), les instructions seront exécutées.
Par cette opération de bind, nous venons de lier une expression régulière à une variable. Par
défaut, une telle expression s’applique à la variable $_ (comme beaucoup de fonctions Perl).
De la même façon,
$v =~ s/voiture/pieds/;
remplace la première occurrence de voiture dans la variable $v par pieds (on verra plus loin
comment remplacer toutes les occurrences). Le reste de $v n’est pas modifié. Le point-virgule
indique la fin de l’instruction.
Pour la correspondance, il existe aussi l’opérateur !~ qui équivaut à =~ suivi d’une négation
de l’expression.
8.3 Caractères
Dans cette sous-partie et dans les suivantes, nous allons voir quels sont les motifs utilisables
dans les expressions régulières.
Dans le cas général, un caractère vaut pour lui-même ; comprenez que lorsque l’on utilise
l’expression régulière m/a/ on vérifie si la variable (ici non citée) contient le caractère a. Cela
semble évident, mais il est bon de le dire.
En effet, pour certains caractères spéciaux, cela n’est pas le cas. Ces caractères ont un rôle
particulier dans les expressions régulières (nous allons voir cela dans la suite). Si vous avez besoin
Motif Caractère
\n saut de ligne
\r retour chariot
\t tabulation
\f saut de page
\e échappement
8.4 Ensembles
Le caractère . (point) correspond à un caractère quel qu’il soit (sauf \n (ce comportement
peut être changé : nous verrons cela plus loin)). Cela signifie qu’à l’emplacement de ce point dans
le motif pourra (devra) correspondre un caractère quelconque dans la variable. Par exemple, le
motif m/t.t./ reconnaı̂tra toute variable comportant une lettre t suivie d’un caractère quel-
conque, puis une autre lettre t, puis un autre caractère quelconque ; par exemple toute variable
comportant une des chaı̂nes suivantes correspondra au motif : tata, t%tK, tot9...
Vous comprenez pourquoi il faut déspécifier le caractère point avec un anti-slash si vous
voulez chercher un point littéral : sans cela un point matche avec n’importe quel caractère.
Le motif [caractères] matche un caractère parmi ceux présents entre crochets. Par exemple
[qwerty] peut reconnaı̂tre une de ces six lettres. Le motif m/t[oa]t[ie]/ reconnaı̂tra toute
variable comportant une des quatre chaı̂nes suivantes : toti, tati, tote ou tate. On comprendra
aisément que si un caractère est présent plusieurs fois dans cette liste, cela a le même effet que
s’il était présent une seule fois : [aeiouyie] est équivalent à [aeiouy].
Il est possible de définir des intervalles de caractères dans ces ensembles. Par exemple a-z
équivaut aux 26 lettres minuscules de l’alphabet. Par exemple [2a-zR] entrera en correspondance
avec toute lettre minuscule ou bien avec le 2 ou bien avec le R majuscule. On peut aussi utiliser
les ensembles A-Z ou 0-9 ; par extension tout intervalle est envisageable, par exemple R-Z ou
toute autre combinaison tant que le numéro ASCII du premier caractère est inférieur à celui
du second. Autre exemple, le motif [ -~] correspond à un caractère ASCII imprimable et de
numéro inférieur à 127.
Un intervalle peut prendre place au milieu d’un motif quelconque :
m/tot[a-zA0-9]V/ matche totaV, totbV... totzV, totAV, tot0V... tot9V.
Si le caractère tiret (-) doit être présent dans l’ensemble, il faut le mettre en première ou en
dernière position afin de lever toute ambiguı̈té possible avec un intervalle. Par exemple [a-z4-]
matche soit une minuscule, soit un 4, soit un tiret.
Le caractère ^ (accent circonflexe) a un rôle particulier s’il est placé en début d’intervalle ; il
prend le complémentaire de l’ensemble, il faut le lire ”tout caractère sauf...”. Par exemple [^ao]
matche tout caractère sauf le a et le o. Le motif [^0-9] matche tout caractère non numérique.
8.5 Quantificateurs
Les quantificateurs s’appliquent au motif atomique (c’est-à-dire le plus petit possible) le
précédant dans l’expression régulière. Ils permettent de spécifier un nombre de fois que ce motif
peut/doit être présent.
Par exemple l’étoile * indique que le motif peut être présent zéro fois ou plus : m/a*/ se met
en correspondance avec le mot vide, avec a, aa, aaa, aaaa...
Quand je dis qu’un quantificateur s’applique au motif atomique le plus petit possible, je veux
dire par là que dans l’expression régulière m/za*/ l’étoile s’applique uniquement à la lettre a et
non au mot za. Nous verrons plus loin comment faire cela.
Il est par ailleurs important de noter qu’un tel quantificateur est par défaut gourmand,
c’est-à-dire qu’il se met en correspondance avec le plus de caractères possible dans la variable
liée. Cela a son importance dans le cas d’une substitution : si la variable $v contient la chaı̂ne
vbaaal, et si on effectue l’instruction suivante : $v =~ s/ba*/hello/; la chaı̂ne matchée par la
première expression ba* sera baaa (le quantificateur matche le plus de caractères possible) et la
substitution aura pour effet de donner pour valeur vhellol à la variable $v.
Voici un tableau des quantificateurs :
On remarquera que * est un raccourci pour {0,} ainsi que + pour {1,}, de même que ? pour
{0,1}.
Dans les exemples précédents, tous les quantificateurs sont appliqués à un caractère. On peut
les appliquer à tout motif, par exemple à un ensemble : m/[0-9-]{4,8}/ recherche une chaı̂ne
comportant entre quatre et huit caractères numériques ou tirets contigus.
8.7 Regroupement
Si dans notre exemple précédent, nous souhaitons rendre optionnelle la partie décimale, on
pourrait écrire : m/[+-]?\d+\.?\d*/ rendant ainsi non obligatoire la présence du point et celle
des chiffres après la virgule. Le problème de cette expression est que la présence du point et de
ces chiffres sont décorrélées : l’expression régulière reconnaı̂tra un nombre où l’une de ces deux
parties serait présente et l’autre absente. Or ce que l’on veut, c’est que le point et les chiffres
qui le suivent soient rendus solidaires dans l’absence ou la présence.
Pour cela nous allons utiliser des parenthèses pour effectuer un regroupement entre plusieurs
motifs (ici le point et les chiffres) pour leur appliquer conjointement le même quantificateur.
L’expression régulière
m/[+-]?\d+(\.\d+)?/ reconnaı̂t donc les nombres tels que nous les souhaitons.
Pour marquer la mémoire de mes étudiants, j’aime à leur dire que
m/meuh{3}/ permet de meugler longtemps et que m/(meuh){3}/ de meugler plusieurs fois !
8.8 Alternatives
Il est possible d’avoir le choix entre des alternatives ; il faut pour cela utiliser le signe pipe
(|) : l’expression m/Fred|Paul|Julie/ reconnaı̂t les mots comportant soit Fred, soit Paul, soit
Julie.
De la même façon, l’expression m/Fred|Paul|Julie Martin/ reconnaı̂t les chaı̂nes compor-
tant soit Fred, soit Paul, soit Julie Martin mais rien n’oblige Fred à s’appeler Fred Martin
ni Paul à s’appeler Paul Martin, comme on aurait sans doute aimé que cela se fasse (dans ces
deux derniers cas, seul le prénom est reconnu, pas le nom). Pour cela, vous l’avez compris, un
regroupement est nécessaire. L’expression régulière m/(Fred|Paul|Julie) Martin/ reconnaı̂t
les trois frères et sœur de la famille Martin.
8.9 Assertions
Une assertion marque une position dans l’expression, elle ne correspond à aucun caractère
(aucune ”consommation” de caractères n’est effectuée).
Par exemple, l’accent circonflexe (^) correspond au début de la chaı̂ne. L’expression $v =~ m/^a/
est vraie si la variable $v commence par la lettre a.
Le signe ^ a donc plusieurs rôles. S’il est au début d’un ensemble entre crochets, il permet
d’en prendre le complémentaire ; s’il est au début de l’expression régulière, il marque le début
de la chaı̂ne. On veillera à ne pas les confondre.
Le dollar ($) correspond à la fin de la chaı̂ne. L’expression $v =~ m/c$/ est vraie si la variable
$v se termine par la lettre c.
my $v = "za aa et tfe";
if( $v =~ /(a+) et ([a-z])/ ) {
print "$1\n"; # ’aa’
print "$2\n"; # ’t’
print "$&\n"; # ’aa et t’
print "$‘\n"; # ’za ’
print "$’\n"; # ’fe’
}
Il est bon de savoir que cela est possible sans obligatoirement se souvenir du nom de toutes
les variables.
($x,$y) = ( $v =~ m/^(A+).*(B+)$/ );
if( $v =~ m/http:\/\/\w+\/(\w+\/)*\w+\.html/ )
if( $v =~ m=http://\w+/(\w+/)*\w+\.html= )
8.16 Options
Après le dernier séparateur des opérateurs de correspondance (m ou rien) ou de substitution
(s) il est possible d’indiquer une ou plusieurs options. Les syntaxes sont donc : m/motif/options
et s/motif1/motif2/options
Les options permettent de modifier le comportement du moteur d’expressions régulières.
Voici la liste de quelques options parmi les plus utiles.
• L’option i rend le motif insensible à la casse (minuscules/majuscules) : l’expression ré-
gulière m/toto/i recherche le mot toto indifféremment en majuscules ou en minuscules.
On aurait pu écrire
m/[tT][oO][tT][oO]/.
• L’option g permet d’effectuer toutes les substitutions dans la variable. Par défaut, l’opé-
rateur s/// effectue la transformation de la première occurrence du motif recherché et ne
va pas plus loin. Si cette option est spécifiée, le moteur d’expressions régulières avancera
dans la variable tant qu’il pourra y faire des substitutions.
Par exemple, l’expression $v =~ s/ +/ /g; remplace chaque groupe de plusieurs espaces
par une seule (contrairement à un des exercices précédents où l’expression régulière ne
remplaçait que la première occurrence du motif trouvé).
Voyez par exemple le code suivant :
$t = $s = "sd et sd";
$t =~ s/sd/toto/; # => "toto et sd"
$s =~ s/sd/toto/g; # => "toto et toto"
L’option g est aussi utilisable en correspondance. Elle permet à cet opérateur de fonction-
ner avec état, c’est-à-dire de poursuivre sa recherche en partant du dernier motif trouvé.
On l’utilise typiquement dans une boucle ; voyez cet exemple :
my $v = "aatobbtbvvtczz";
while( $v =~ m/t./g ) {
print "$&\n";
}
L’affichage effectué est le suivant :
to
tb
tc
• Dans une substitution, l’option e évalue le membre de droite comme une expression Perl,
et remplace le motif trouvé par la valeur de cette expression. Par exemple :
$s =~ s/(\d+)/fonction($1)/e;
$s = "velo";
if( $v =~ m/$s$/ ) { ... }
Cela peut aussi poser des problèmes de sécurité si le programmeur ne sait pas ce que peut
contenir la variable substituée (par exemple si sa valeur provient de l’utilisateur). Il existe pour
cela une fonction quotemeta qui prend en paramètre une chaı̂ne de caractères et renvoie cette
même chaı̂ne en ayant déspécifié les caractères spéciaux.
$s = "fds(ds";
$s2 = quotemeta($s);
print "$s2\n"; # affiche fds\(ds
if( $v =~ m/$s2/ ) { ... }
Pensez à toujours utiliser cette fonction lorsque vous voulez placer une variable dans un
motif ; cela résout bien des problèmes.
$s = "azerty";
$s =~ tr/abcde/01234/;
print "$s\n"; # affiche 0z4rty
Dans la variable $s, tous les a seront transformés en 0, tous les b en 1, etc, tous les e en 4,
etc.
Il est possible d’utiliser des intervalles : $s =~ tr/a-z/A-Z/; met par exemple le contenu
de la variable en majuscules. C’est l’un des seuls usages courants de l’opérateur tr.
Références
Les références permettent de bâtir des structures complexes et composées : tableau de ta-
bleaux ou de tables de hachage et inversement, table de hachage de tableaux ou de tables de
hachage...
Le terme de référence en Perl correspond à peu près à celui de pointeur en C et C++ et à
celui de référence en Java. Les habitués du C ou C++ noteront que les calculs sur références
sont interdits en Perl, ce qui permet d’éviter toutes sortes de problèmes dus à des accès mémoire
erronés (plus de Segmentation fault).
Chaque variable, qu’elle soit scalaire, tableau ou table de hachage, est présente à une posi-
tion donnée dans la mémoire. Une référence vers une variable est (schématiquement) l’adresse
mémoire de cette variable. Une telle référence peut elle-même être stockée dans une variable
scalaire.
my $refv = \$v;
Ici la variable $refv (on aurait pu choisir un tout autre nom pour cette variable) est une
référence vers la variable $v. Une variable de type référence, quel que soit celui de la variable
qu’elle référence (scalaire, tableau ou table de hachage), est un scalaire. On peut représenter la
relation entre ces deux variables de la façon suivante :
$refscalaire
$v
−43.5
73
Guide Perl https://formation-perl.fr/
Il nous est alors possible de manipuler $v au travers de $refv. La notation $$refv (donc
avec deux dollars) est équivalente à $v tant que $refv pointe vers $v. Autrement dit, la nota-
tion $$refv équivaut à la variable scalaire pointée par la référence $refv. Dit de manière plus
prosaı̈que, on va accéder à la variable qu’il y a ”au bout” de la référence (au bout de la flèche
du schéma). On dit alors que l’on déréférence la variable $refv. Pour faire un parallèle avec le
langage C, il s’agit de l’équivalent de l’étoile (*) appliquée à un pointeur.
Revenons sur notre exemple. Nous déclarons une variable scalaire $v que nous initialisons.
Nous déclarons ensuite une autre variable scalaire $refv à laquelle on affecte l’adresse de $v ;
$refv est donc une référence sur $v :
my $v = -43.5;
my $refv = \$v;
On voit bien alors que la référence pointe vers une variable de type scalaire (SCALAR) dont
l’adresse mémoire est affichée en hexadécimal. Affichons maintenant la variable pointée par $refv
(c’est-à-dire $v ici) :
L’affichage effectué est -43.5 (c’est-à-dire la valeur de $v). Cette notation $$refv est
équivalente à $v puisque $refv est une référence sur $v. Cette équivalence vaut aussi bien lorsque
l’on a besoin de la valeur de $v (affichage, etc.) que lorsque l’on veut affecter une nouvelle valeur
à $v :
On affecte 56 à $$refv, c’est-à-dire à $v. Si on affiche cette variable, on voit bien qu’elle
contient cette nouvelle valeur :
sub f {
my ($ref) = @_;
$$ref = 0;
}
f( $refv );
Ce qui aurait pour effet de mettre la variable $v à la valeur 0. On pourrait aussi écrire
directement :
f( \$v );
sub f2 {
my $w = 43;
return \$w;
}
Cette fonction f2 déclare une variable locale $w et renvoie une référence vers cette variable.
Contrairement à ce qu’il se passe en C, ceci est tout à fait légal et sans risque en Perl. Voici
comment utiliser cette fonction :
my $reff = f2();
La variable scalaire $reff devient donc une référence vers une variable scalaire valant 43.
Cette variable scalaire valant 43 est l’ancienne variable $w de la fonction f2. La variable $reff
pointe donc vers une variable qui n’a plus de nom : dans f2 elle s’appelait $w, mais en dehors
de l’appel à cette fonction qui l’a créée, elle n’a plus de nom.
$reff
En temps normal, une variable locale à une fonction est détruite lorsque l’on sort de la
fonction. Mais tant qu’il existe une référence vers la variable, elle est conservée en mémoire.
C’est le garbage collector (ramasse-miette ou glaneur de cellules) qui la libérera lorsque plus
aucune référence sur la variable n’existera.
Il faut noter que lors d’un prochain appel à la fonction f2, une autre variable $w sera créée
indépendante de la première ; il n’y aura donc pas d’effet de bord sur la première référence
renvoyée. Nous sommes ici en présence d’une fonction qui peut faire office de générateur de
références sur scalaire ;-)
$reft
@t
0 23 1 ab 2 −54.4
Pour déréférencer une telle référence, il convient d’utiliser une arobase (@) : @$reft est
équivalent à @t. On peut ainsi utiliser la valeur de @t en utilisant $reft :
my @t2 = @$reft;
foreach my $e (@$reft) { .... }
Si, pour accéder au ième élément de @t, il était possible d’écrire $t[i], il est maintenant
possible d’écrire $$reft[i]. On peut alors dire, de manière schématique et pour fixer les choses,
que la notation $reft est équivalente au nom t de la variable dans toutes les syntaxes utilisant
ce tableau (partout où l’on peut écrire t, on peut écrire $reft à la place, tant que $reft pointe
sur @t). Voici, en effet, un récapitulatif des équivalences de notations :
Tableau Référence
t $reft
@t @$reft
$t[i] $$reft[i]
$t[i] $reft->[i]
Cette dernière notation $reft->[i] est équivalente à $$reft[i] et correspond donc au ième
élément du tableau référencé par $reft. C’est la notation la plus souvent utilisée pour cela ; elle
rappelle la même notation flèche (->) du langage C.
L’expression
$reft->[1] = "coucou";
affecte donc à l’élément d’indice 1 du tableau pointé par $reft une nouvelle valeur.
Muni des références, il va maintenant nous être possible de créer des tableaux de tableaux.
Cela était pour le moment impossible en raison de l’aplatissement des listes. Une référence étant
un scalaire, il va nous être possible de stocker une référence comme valeur dans un tableau :
@t
0 1 2 3
6 s
0 el 1 0.3 2 4
0 16 1 −33
Vous noterez bien que la syntaxe suivante correspond à la création d’un tableau à sept
éléments (aplatissement des listes) :
my @t2 = ( 6,
( 16, -33 ),
( "el", 0.3, 4 ),
"s" );
Nous verrons dans la suite d’autres syntaxes pour construire des tableaux de tableaux.
La variable scalaire $refh est donc une référence vers la table de hachage %h :
$refh
%h
Julie => 19
Paul => 21
Pour déréférencer une référence vers une table de hachage, il faut utiliser le caractère pour-
centage (%) ; %$refh est équivalent à %h :
Les deux notations suivantes permettent d’accéder à la valeur associée à la clef Paul :
$$refh{Paul} et $refh->{Paul} sachant que la seconde est la plus utilisée pour des raisons
identiques aux cas des tableaux.
$refh->{Jacques} = 33;
Hash Référence
h $refh
%h %$refh
$h{Paul} $$refh{Paul}
$h{Paul} $refh->{Paul}
Voici une façon d’afficher tous les couples clef/valeur de la table de hachage référencée par
$refh :
Comme une référence peut pointer vers n’importe quel type de structure (scalaire, tableau,
table de hachage), cette vérification ne peut avoir lieu qu’au moment de l’exécution.
De plus, les habitués du langage C seront invités à se mettre une bonne fois pour toutes dans
la tête :-) qu’il n’y a pas d’arithmétique possible sur les références en Perl. Ajouter 1 à une
référence ne correspond pas à pointer vers l’élément d’indice 1 d’un tableau, mais à faire perdre
le caractère référence à la variable (elle devient un scalaire comme un autre) :
$r
@t
0 1 2 −2
49 hello
32.3
$reftab
Visibilité de la fonction f3
my $ref1 = \34;
my $ref2 = \"er";
print "$$ref1 $$ref2\n";
$ref1
34
$ref2
er
Une telle valeur est une constante, il est impossible de modifier la valeur pointée par la
référence (différence avec le mécanisme de la fonction f2) :
$$ref1 = "hello";
# Modification of a read-only value attempted at script.pl line 24.
Ce n’est pas dans le cas des scalaires que les références anonymes sont les plus utilisées ; elles
le sont bien plus avec les tableaux et les tables de hachage.
La variable $r est une référence vers un tableau comportant trois éléments alors que la
variable @t est un tableau à trois éléments (cette dernière notation nous est déjà familière) :
0 1 2
34.4 ac −71
@t
0 1 2
34.4 ac −71
On accède aux éléments de cette référence anonyme comme pour une référence normale :
print "$r->[0]\n";
$r->[2] = 901;
foreach my $e (@$r) { ... }
Attention à ne pas écrire \(2,"er",$v) si vous voulez créer une référence vers un tableau,
car cette syntaxe fait toute autre chose : elle est en fait équivalente à (\2,\"er",\$v), donc à
une liste de références, ce qui est fort différent.
La structure précédemment décrite par
my @t = ( 6,
[ 16, -33 ],
[ "el", 0.3, 4 ],
"s" );
@t
0 1 2 3
6 s
0 1 2
el 0.3 4
0 16 1 −33
On peut pousser le vice jusqu’à utiliser une référence pour le premier tableau :
0 1 2 3
6 s
0 el 1 0.3 2 4
0 1
16 −33
Comment accéder aux éléments des différentes profondeurs d’une telle construction ? Il suffit
de suivre les références...
print "$r->[0]\n"; # affiche 6
# $r->[1] est une référence vers tableau
print "$r->[1]->[0]\n"; # affiche 16
print "$r->[1]->[1]\n"; # affiche -33
# $r->[2] est une référence vers tableau
print "$r->[2]->[0]\n"; # affiche el
print "$r->[2]->[1]\n"; # affiche 0.3
print "$r->[2]->[2]\n"; # affiche 4
print "$r->[3]\n"; # affiche s
Ce n’est pas si complexe qu’il n’y paraı̂t pour peu que nous ayons un bon schéma sous la
main... Vous noterez que nous faisons usage de l’opérateur flèche (->) plutôt que de la syntaxe
double-dollar ($$).
De plus, si $r->[1] est une référence vers tableau, @{$r->[1]} est le tableau en question.
On peut donc écrire :
foreach my $e ( @{$r->[1]} ) { ... } # Parcourt 16 et -33
Il faut noter qu’en cas de déréférencements successifs, seule la première flèche est nécessaire :
$r->[2][1] est équivalent à $r->[2]->[1].
La variable $r est une référence vers une table de hachage alors que la variable %h est une
table de hachage (notation familière) :
$r
Julie => e
Paul => 21
%h
Julie => e
Paul => 21
De la même façon qu’avec des tableaux, nous allons mettre sur pied des tables de hachage
de tables de hachage :
Ou plus directement :
my $r = {
’Paul’ =>
{ ’rue’ => ’Pasteur’,
’tel’ => ’06461341’ },
’Julie’ =>
{ ’rue’ => ’Jaures’,
’tel’ => ’03729103’ }
};
Julie =>
Paul =>
my $r = [
[’a’,4],
’b’,
[1,’z’],
{’P’=>[-2,"er",0],’A’=>7},
$r
0 1 b 2 3 4
8
0 1
1 z
0 a 1 4 A => 7
P =>
0 1 2
−2 er 0
Les crochets correspondent à une prise d’indice d’un tableau ; les accolades à la clef d’une
table de hachage.
Je peux parcourir le premier tableau du deuxième niveau (celui qui comporte a et 4) de la
manière suivante :
my $reft = $r->[0];
foreach my $v (@$reft) {
print "$v\n";
}
Je crée une variable $reft qui est une référence vers ce tableau, je peux ensuite parcourir
@$reft qui représente le tableau en question.
Il est possible d’écrire cela sans créer de variable temporaire, la syntaxe est la suivante :
@{référence} Ces accolades n’ont rien à voir avec les tables de hachage elles permettent juste de
délimiter la référence à laquelle on applique l’arobase. Voici ce que cela donne :
On fait de la même façon pour une table de hachage, les accolades délimitent la référence à
laquelle on applique le pourcentage :
foreach my $p (@$r) {
if( ref($p) eq "ARRAY" ) {
print "( ";
foreach my $v (@$p) {
print "$v ";
}
print ")\n";
} elsif( ref($p) eq "HASH" ) {
foreach my $k (keys(%$p)) {
print "$k : $p->{$k}\n";
}
} elsif( !ref($p) ) {
print "$p\n";
}
}
( a 4 )
b
( 1 z )
Dans cet exemple, un seul premier niveau de références est exploré. Pour aller au-delà, c’est-
à-dire, afficher le tableau associé à la clef P, il faudrait concevoir un ensemble de fonctions
s’appelant les unes les autres en fonction du type des références rencontrées. Ne vous fatiguez
pas à les écrire, il existe déjà une telle fonctionnalité en Perl :
use Data::Dumper;
print Dumper($r);
La première ligne ajoute des fonctions à Perl (c’est un peu le #include du langage C) et
ne doit donc être présente qu’une seule fois dans le programme (plutôt au début). La seconde
consiste en l’affichage de la valeur de retour de la fonction Dumper : cette fonction renvoie une
(longue) chaı̂ne de caractères représentant toute la structure référencée par $r :
$VAR1 = [
[
’a’,
4
],
’b’,
[
1,
’z’
],
{
’P’ => [
-2,
’er’,
0
],
’A’ => 7
},
8
];
Vous noterez que l’affichage effectué est directement intégrable dans un code Perl.
0 71 1
*
Od =>
Ptt =>
On voit bien alors qu’un cycle de références existe entre ces références. Voici comment écrire
cela en Perl :
my $r = [
71,
{
"Hello" => -34.7,
"Ptt" => { "R5" => "As4" }
}
];
$r->[1]{Ptt}{Od} = $r;
On comprend bien qu’il n’est pas possible de créer une telle structure en une seule instruction.
Comment se comporte Data::Dumper dans une telle situation ? Ne va-t-il pas boucler à l’infini ?
Eh bien non : il se comporte bien :
print Dumper($r);
$VAR1 = [
71,
{
’Ptt’ => {
’R5’ => ’As4’,
’Od’ => $VAR1
},
’Hello’ => ’-34.7’
}
];
$r = undef;
En temps normal, tout ce que $r référençait serait libéré. Mais ici, ce n’est pas le cas. En
effet, chacun des tableaux et des tables de hachage a encore au moins une référence vers eux, le
garbage collector ne se rend pas compte, qu’en fait, les trois objets peuvent être libérés. Nous
sommes donc en présence de zones mémoires inaccessibles (aucune variable ne nous permet d’y
accéder) et non libérables : cette mémoire est perdue pour le programme ! Elle sera bien sûr
libérée quand le programme prendra fin, mais s’il s’agit d’un démon qui tourne en permanence,
cette fuite mémoire n’est pas forcément à négliger.
La solution pour éviter cela est de ”casser” la circularité avant de modifier la valeur de la
variable $r :
$r->[1] = undef;
Je viens de casser le lien indiqué par un astérisque, il n’y a plus de boucle dans les références.
Maintenant et seulement maintenant, je puis sans risque écrire :
$r = undef;
open($fd,">toto") or die("$!");
la variable scalaire $fd est déjà une référence. C’est une référence vers des données que nous
n’avons pas à manipuler directement, mais c’est une référence.
Dans des vieilles version Perl, nous pouvions écrire une ouverture de fichier avec une variable
qui ne comporte par de sigil :
Nous pouvons créer prendre une référence vers cette variable. Voici la syntaxe pour cela :
my $reff = \*FILE;
La variable scalaire $reff est une référence vers le descripteur de fichier FILE. Il nous est
aussi possible de créer une référence vers un des quatre fichiers préexistants :
open(FILE,">toto") or die("$!");
my $reff = \*FILE;
print $reff "ok\n";
sub affiche {
my ($ref) = @_;
print $ref "ok\n";
}
affiche( $reff );
affiche( \*FILE ); # équivalent
close( $reff );
Cette dernière ligne est équivalente à close(FILE); On peut sans restrictions stocker une
référence vers fichier dans une table de hachage ou dans un tableau.
sub affcoucou {
my ($p) = @_;
print "Coucou $p\n";
}
my $ref = \&affcoucou;
La variable scalaire $ref est une référence vers la fonction affcoucou. Elle peut s’utiliser
des façons suivantes :
&$ref("Larry"); # appel
$ref->("Larry"); # équivalent
sub f {
my ($f,$p) = @_;
$f->( $p );
}
f( $ref, "Larry" );
f( \&affcoucou, "Larry" ); # équivalent
Notez bien qu’il est tout à fait possible de stocker une telle référence dans un tableau ou
dans une table de hachage...
Les modules
Nous allons aborder ici l’usage et l’écriture de modules, c’est-à-dire de bibliothèques ou encore
librairies. Perl tire sa puissance de la richesse des modules existants ; peu d’autres langages
(voire aucun) ne peuvent prétendre être aussi riches que Perl. Par exemple, quasiment tous les
protocoles réseau auxquels vous pouvez penser sont accessibles en Perl en utilisant un module
existant.
En quelques mots, un module est un ensemble de fonctions regroupées, dans un fichier. Ces
fonctions y sont regroupées car elles touchent toutes à un même domaine, à un même ensemble
de fonctionnalités autour d’une même utilisation, d’un même protocole...
La première chose que je vous invite à faire, c’est de lancer la commande perl -V : elle
affiche toutes sortes d’informations, dont le contenu de la variable @INC. Cette variable de type
tableau contient la liste des répertoires où seront recherchés les modules. Le nom INC rappelle
étrangement (et c’est voulu) la notion d’include en C. L’ordre des répertoires de cette variable
est important car si un module vient à être présent dans deux répertoires, seule l’occurrence
présente dans le premier répertoire de la liste comptera (mais ce cas proviendrait plutôt d’une
erreur de nommage ou d’installation).
use NomDuModule;
93
Guide Perl https://formation-perl.fr/
Dans bien des cas, cette instruction ajoute des fonctions et des variables à l’espace de nom-
mage (nous reviendrons sur ce point dans la suite). Pour notre exemple, la ligne est :
use Math::Trig;
Cette ligne devra être placée dans chaque script qui fait usage du module et être exécutée
avant tout usage de fonctions ou de variables du module. Typiquement toutes les lignes use sont
regroupées au début du script.
Vous remarquerez que la ligne use strict; que je vous ai conseillé de placer dans chaque
script, n’est en fait que le chargement d’un module ; ce module ayant pour rôle de rendre la
syntaxe Perl plus coercitive. Le nom des modules de ce type est en minuscules. Ils sont ap-
pelés modules pragmatiques. Ils ont pour objet de modifier ou d’étendre la sémantique de Perl.
Ainsi diagnostics permet d’avoir des messages d’erreurs plus complets (vous pouvez charger
sur CPAN la version 1.2-alpha1 qui vous permet d’avoir ces messages avec des explications en
français).
Revenons à notre module Math::Trig. Voici un exemple de code Perl l’utilisant (j’en plagie
ici la documentation) :
use Math::Trig;
$x = tan(0.9);
$y = acos(3.7);
$z = asin(2.4);
$pi_sur_deux = pi/2;
$rad = deg2rad(120);
Je laisse au lecteur le soin de deviner (ou plutôt comprendre) ce que font ces instructions.
Une fois chargé, un module n’est pas ”déchargeable”.
use File::Copy;
copy("file1","file2");
copy("Copy.pm",\*STDOUT);’
move("/dev1/fileA","/dev2/fileB");
Voici un autre exemple de module en action. Il s’agit du module Net:FTP qui nous permet
d’accéder très simplement aux fonctionnalités d’un client FTP. Voici, par exemple, comment se
connecter sur un serveur (en mode passif, car j’ai un firewall), changer de répertoire et télécharger
un fichier :
#!/usr/bin/perl
use strict;
package Utils;
Il est important de voir que le nom du package doit être le même que celui du fichier (à
l’extension près). Le fichier peut ensuite contenir des définitions de fonctions. Voici un exemple
simple d’un tout petit module complet :
Il est important de ne pas oublier la dernière ligne, celle qui contient 1; ; nous reviendrons
plus tard sur son rôle.
Pour pouvoir utiliser ce module dans un script, il est nécessaire d’invoquer l’instruction use
suivie du nom du module. Voici un exemple de l’utilisation du module précédent :
#!/usr/bin/perl
# --- fichier script.pl ---
use strict;
use warnings;
use Utils; # chargement du module
Utils::bonjour( "Paul" );
Que la variable soit déclarée avec my ou avec our, il est tout à fait possible d’y accéder
depuis une fonction du module (ici bonjour). À l’inverse, depuis l’extérieur du module, c’est-à-
dire depuis le script, seule la variable $x est accessible.
#!/usr/bin/perl
# --- fichier script.pl ---
use strict;
use warnings;
use Utils;
Utils::bonjour( "Paul" );
# Ok :
print "$Utils::x\n";
# Erreur :
print "$Utils::y\n";
De même que pour les fonctions, les noms de variable sont préfixés par le nom du module
puis deux signes deux-points. Ici, le nom complet de la variable est donc Utils::x qu’il faut
faire précéder d’un signe dollar, ce qui donne : $Utils::x au final. Il n’y a pas d’erreur de ma
part : on n’écrit pas Utils::$x ! :-)
10.7 Répertoires
Voyons maintenant comment créer des modules aux noms composés comme Truc::Utils
(nous avons par exemple vu le module Net::FTP). Ces noms composés permettent de regrouper
les modules par type d’usages ; par exemple Net correspond à tout ce qui concerne le réseau.
Revenons à notre exemple Truc::Utils. Ce nom Truc correspond à un répertoire qui doit
être présent dans un des répertoires de la variable @INC (par exemple .) et le fichier Utils.pm
doit être présent dans ce répertoire Truc.
Voici un exemple de tel module :
#!/usr/bin/perl
# --- fichier script.pl ---
use strict;
use warnings;
use Truc::Utils;
Truc::Utils::bonjour( "Paul" );
print "$Truc::Utils::x\n";
Rien de sorcier.
package Utils;
use strict;
use warnings;
sub f {
...
}
BEGIN {
print "Chargement du module\n";
}
END {
print "Fin d’usage du module\n";
}
1;
Notez bien qu’il ne s’agit pas de fonctions (pas de mot clef sub), mais bien de blocs labélisés.
Le bloc BEGIN sera exécuté lors de l’instruction use Utils; avant toute autre instruction du
module (y compris les use placés dans le module). Le bloc END sera exécuté lors de la fin du
programme.
L’usage de ces deux blocs peut être nécessaire lorsque l’utilisation du module est conditionnée
par l’obtention d’une ou plusieurs ressources comme un fichier ou une connexion réseau. Ces blocs
vont nous servir à préparer le terrain au début et à libérer les ressources à la fin.
Lorsque dans un module sont présentes d’autres instructions que des définitions de variables
et des définitions de fonctions, ces instructions sont exécutées au moment du chargement du
module. Tout se passe comme si ces instructions figuraient dans un BEGIN implicite. L’usage
d’un bloc BEGIN permet juste au programmeur d’écrire un code un peu plus lisible et propre,
dans la mesure où toutes ces instructions sont regroupées sous un nom (BEGIN) qui rappelle
explicitement qu’elles sont exécutées au début.
Notez bien que la programmation objet (lire la suite) a quelque peu rendu ces deux blocs
obsolètes, voire inutiles.
package Utils;
use Exporter;
our @ISA = qw(Exporter);
Ces deux nouvelles lignes d’instructions doivent être placées juste après l’instruction package.
La première est l’invocation du module Exporter ; avec la seconde on indique que notre module
est un (ISA) Exporter (nous reverrons cette syntaxe et ses implications en programmation
objet). Notre module est maintenant capable d’exporter des symboles.
Il existe quatre types de symboles (fonctions ou variables) :
• ceux qui sont exportés par défaut : le script utilisant le module n’a besoin de rien faire
de spécial (autre que de faire le use) pour que ces symboles soient exportés,
• ceux qui sont individuellement exportables en fonction de ce que demande le script utili-
sateur ;
• ceux qui sont exportables en groupe (on parle de tags) selon ce que demande le script
utilisateur ;
• ceux qui ne sont pas exportables (c’est-à-dire qu’il faudra toujours faire précéder leur
nom par le nom complet du module).
Chacun des trois premiers ensembles est associé à une variable déclarée avec our.
va permettre d’utiliser les fonctions bonjour et hello ainsi que la variable scalaire $var sans
préfixe dans le script utilisateur. Notez bien que, si les variables doivent être citées avec leur
caractère de différentiation de type (le dollar, l’arobase ou le pourcentage), il en est de même
avec les fonctions et le signe ≪ esperluette ≫ (&). Sachez juste que cet esperluette peut être omis.
Voici comment on peut maintenant utiliser le module en question :
use Utils;
bonjour("Paul");
hello("Peter");
print "$var\n";
Plutôt simple.
Ces trois symboles sont maintenant exportables dans le script utilisant ce module. Pour cela
il convient d’ajouter une liste de symboles à l’instruction use :
Cette ligne importe donc les symboles demandés et ces derniers sont donc utilisables sans
préfixe.
Il se trouve qu’une telle ligne n’importe plus les symboles par défaut (ceux de la variable
@EXPORT) ; ne me demandez pas pourquoi, je trouve cela aussi stupide que vous... Pour remédier
à cela, il nous faut ajouter à la liste des imports le tag :DEFAULT :
Le tag T1 est associé aux fonctions ciao et gutenTag. Le tag T2 est associé à la fonction
ciao et à la variable $var2. Le nom des tags est par convention en majuscules.
Remarque importante : les symboles présents dans les listes associées aux tags doivent ab-
solument être présents dans @EXPORT et/ou @EXPORT_OK. Dans le cas contraire, leur export sera
impossible.
Voici un usage de ce module :
Le nom du tag est placé dans la liste des modules, précédé par le signe deux-points. Il est
possible de combiner les différents types d’accès :
package Utils;
use strict;
use warnings;
use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(&f1 &f2);
our @EXPORT_OK = qw(&f3 &f4 &f5 &f6);
our %EXPORT_TAGS = (T1 => [qw(&f5 &f6)],
T2 => [qw(&f4 &f6)]);
sub f1 { print "f1\n"; }
sub f2 { print "f2\n"; }
sub f3 { print "f3\n"; }
sub f4 { print "f4\n"; }
sub f5 { print "f5\n"; }
sub f6 { print "f6\n"; }
1;
Et voici un script l’utilisant (après chaque appel de fonction est signalée la raison qui fait
qu’il est possible de l’appeler sans préfixe) :
#!/usr/bin/perl
use strict;
use warnings;
use Utils qw(:DEFAULT :T2 &f3);
Notez bien le cas de la fonction f5 qui n’est pas importée, mais qui n’en reste pas moins
utilisable.
Pour plus d’informations et d’exemples je vous invite à vous référer à perldoc Exporter.
package Utils;
use strict;
use warnings;
my $affiche = sub {
my ($n,$m) = @_;
print "$n, $m\n";
};
La variable $affiche est donc une variable privée qui pointe vers une fonction anonyme.
Son usage est donc réservé aux fonctions déclarées dans le module :
sub truc {
$affiche->(4,5);
}
Remarquez que, comme le code Perl est toujours accessible en lecture, il est toujours possible à
l’utilisateur du module de prendre le code en copier-coller et d’en faire une fonction personnelle...
Perl n’est pas fait pour les paranoı̈aques.
=head1 NAME
=head1 SYNOPSIS
use Utils;
bonjour("Paul");
=head1 DESCRIPTION
Blabla blabla
=head2 Exports
=over
=back
=cut
Les tags =head1 définissent des en-têtes de premier niveau (des gros titres) et les tags =head2
définissent des en-têtes de deuxième niveau (des sous-titres). Il est de coutume de mettre les
premiers exclusivement en majuscules. Les tags =over, =item et =back permettent de mettre en
place une liste. Le reste du texte est libre. Le tag =cut indique la fin du POD.
Les blocs de POD et les portions de code peuvent alterner : cela est même recommandé de
documenter une fonction et d’en faire suivre le code. Pour cela vous devez savoir que l’apparition
en début de ligne d’un tag POD indique la fin temporaire du code Perl et le début d’un bloc
de documentation. La fin de ce POD est signalée à l’aide du tag =cut et le code Perl peut alors
reprendre.
package Utils;
=cut
sub hello {
my ($firstName) = @_;
print "Hello $firstName\n";
}
=cut
sub bonjour {
my ($prenom) = @_;
print "Bonjour $prenom\n";
}
Comment visualiser une telle documentation, allez-vous me demander ? Rien de plus simple :
perldoc est notre allié ! Tapez donc perldoc Utils (ou tout autre nom que vous aurez choisi
de donner à votre module) et sa documentation apparaı̂t au format man comme tout bon module
CPAN. Quoi de plus simple ?
NAME
Utils.pm - Useful functions
SYNOPSIS
use Utils;
bonjour("Paul");
DESCRIPTION
Blabla blabla
Exports
:T1 Blabla
:T2 Blabla
FUNCTION hello
This function prints hello.
FUNCTION bonjour
This function prints hello in french.
Pour plus d’informations et de détails sur ce format, je vous invite à consulter perldoc perlpod
où de nombreux exemples sont donnés. Vous pouvez aussi jeter un œil au code d’un module ou
deux dont le perldoc vous intrigue...
Programmation objet
La programmation objet est le concept nouveau de ces vingt dernières années. C++ est bâti
sur le C et apporte l’objet. Java a été mis au point (entre autres) pour passer outre les nombreux
pièges et problèmes de C++. Ruby est un langage interprété basé sur ce concept objet.
Perl, qui utilisait un garbage collector bien avant que Java n’existe, ne pouvait pas être en
reste et la communauté Perl a rapidement proposé les extensions du langage nécessaires à ce
type de programmation.
On notera que ces extensions sont peu nombreuses, car l’idée a été de réutiliser au maximum
ce qui existait déjà en Perl et de l’appliquer à la programmation objet. Le C++ étant compilé
et devant rester compatible avec le C, cela fut un challenge de mettre sur pied ce langage ; cela
explique sans doute pourquoi C++ est si complexe et comporte tant de pièges. Perl, de par sa
nature interprétée, n’a pas posé de problème pour s’étendre à l’objet.
La programmation par objets ouvre le champ des possibilités offertes au programmeur ;
alliée à un langage puissant et flexible comme Perl, elle offre la souplesse, la richesse et la facilité
d’écriture qu’il manque aux langages uniquement objet. Toutefois, de par sa nature permissive,
le langage Perl ne saurait être aussi strict que des langages exclusivement objet. Le programmeur
est invité à faire les choses proprement, mais rien ne l’y oblige.
107
Guide Perl https://formation-perl.fr/
dont la pointure, la couleur et la matière sont renseignées.
11.2 Préparatifs
Nous allons maintenant voir comment écrire une classe en Perl. Vous verrez, cela est très
simple et démystifie la programmation objet.
En Perl, une classe n’est autre qu’un module et un objet (instance de cette classe) n’est autre
qu’une référence associée à cette classe. Dans le constructeur, nous allons donc créer une référence
(typiquement vers une table de hachage) et nous allons l’associer au package en question ; lors
de cette association, on dit en Perl que l’on bénit (bless en anglais) la référence.
Les champs de l’objet seront en fait stockés dans cette table de hachage, sous forme de la
clef pour le nom du champ et de la valeur pour la valeur du champ.
Voyons un exemple : définissons une classe Vehicule qui comporte deux champs : un nombre
de roues et une couleur.
La ligne numéro 2 indique le nom du package actuel ; il est conseillé de choisir le même nom
que pour le fichier, simplement pour des raisons d’organisation et de maintenance. La ligne 13
comporte le fameux code de retour du chargement du module. La ligne 3 force une syntaxe
plus rigoureuse. En Perl, le nom des modules et donc des classes que le programmeur définit
doit être composé de majuscules et de minuscules, avec typiquement une majuscule au début de
chaque mot ; les noms de package exclusivement en minuscules sont réservés pour les modules
pragmatiques de Perl (dits modules pragma comme strict, etc.), les noms exclusivement en
majuscules sont inélégants. :-)
Nous définissons une fonction new (ligne 5) dont le but est de construire un objet Vehicule.
Il s’agit donc d’un constructeur ; un constructeur en Perl est une simple fonction renvoyant un
objet. Il est bon de noter que le choix du nom pour cette fonction est totalement libre ; il est
courant de l’appeler new, mais rien ne nous y oblige. On pourrait par exemple choisir le nom de
#!/usr/bin/perl
use strict;
use warnings;
Comme pour tout module, nous devons explicitement indiquer que nous allons l’utiliser :
use Vehicule;
my $v = Vehicule->new( 2, "bleu" );
my $v2 = Vehicule->new( 4, "rouge" );
$v
Vehicule
NB_ROUES => 2
$v2
Vehicule
NB_ROUES => 4
Cette syntaxe Vehicule->new correspond à l’appel du constructeur new que nous venons
d’écrire. La variable $v est initialisée à la valeur de retour de cette fonction. Elle est donc une
référence vers une table de hachage dont deux champs sont initialisés et qui a été bénie (bless)
en Vehicule. Idem pour $v2.
Il est aussi possible de faire usage de la syntaxe suivante :
Cette formulation va sans doute rassurer les habitués de Java ou de C++, mais peut induire
le programmeur en erreur. En effet, cette dernière syntaxe semble indiquer que new est un opé-
rateur spécifique pour appeler un constructeur et est donc un mot réservé du langage. Il n’en
est rien ; comme on le verra un peu plus loin, ce nom est totalement arbitraire.
print "$v\n";
Vehicule=HASH(0x80f606c)
Je vous rappelle que dans le cas de l’affichage d’une référence vers une table de hachage non
bénie, nous obtenons quelque chose de la forme :
Un objet (à partir de maintenant, nommons ainsi une référence vers une table de hachage
bénie) sait donc de quelle classe il est. Cela va lui permettre de choisir le bon package quand on
appellera une méthode sur cet objet (lire la suite).
Voyons maintenant ce que donne le module Data::Dumper (dont j’ai déjà parlé) sur une telle
référence :
use Data::Dumper;
print Dumper($v)."\n";
$VAR1 = bless( {
’COULEUR’ => ’bleu’,
’NB_ROUES’ => 2
}, ’Vehicule’ );
my $w = $v;
$v
Vehicule
NB_ROUES => 2
$w
COULEUR => bleu
$v2
Vehicule
NB_ROUES => 4
La modification de l’objet pointé par l’un modifiera celui pointé par l’autre, car il s’agit du
même objet.
sub nouveau {
my ($class,$couleur) = @_;
my $this = {};
bless($this, $class);
$this->{COULEUR} = $couleur;
return $this;
}
Ce constructeur de Vehicule prend par exemple un seul paramètre (la couleur) et n’affecte
pas de champs NB_ROUES, car rien de ne l’y oblige. À moi de savoir comment je veux gérer mes
véhicules, à savoir comment j’autorise qu’ils soient construits.
Pour être propre et cohérent, nous allons tout de même considérer qu’il est sage d’affecter
une valeur à la clef NB_ROUES :
sub nouveau {
my ($class,$couleur) = @_;
my $this = {};
bless($this, $class);
$this->{NB_ROUES} = 0;
$this->{COULEUR} = $couleur;
return $this;
}
De la même façon que pour le précédent constructeur, il est possible d’utiliser la syntaxe
suivante :
Ce qui est, reconnaissons-le, quelque peu déroutant. C’est pour cela que je vous conseille d’uti-
liser plutôt la première syntaxe Vehicule->nouveau() ou alors de vous en tenir à un constructeur
new pour écrire new Vehicule().
Cette fonction déclarée dans le fichier Vehicule.pm a donc pour premier paramètre l’objet
sur lequel elle est appelée. Vous noterez une fois de plus que rien n’oblige le programmeur à
nommer cette variable $this ; la seule contrainte est sa première place parmi les arguments.
Cette méthode peut dès maintenant être appelée depuis le fichier
script.pl sur les objets de type Vehicule. Il n’y pas de nécessité de faire usage du lourd
mécanisme d’Exporter, car nous n’avons pas besoin de modifier l’espace de nom des fonctions
des scripts appelants.
Pour appeler la méthode, il suffit d’écrire dans le fichier script.pl :
$v->roule( 15 );
La fonction appelée sera celle qui a pour nom roule définie dans le package lié à la référence
$v lors de sa bénédiction.
L’affichage suivant a donc lieu :
sub toString {
my ($this) = @_;
return "(Vehicule:$this->{NB_ROUES},$this->{COULEUR})";
}
Cette méthode renvoie une chaı̂ne de caractères, représentation de l’objet. Les habitués de
Java noteront que j’ai choisi le nom de cette fonction pour leur rappeler des souvenirs, mais qu’il
n’a rien de spécial. Voici comment l’utiliser dans le script :
print $v->toString()."\n";
Et l’affichage a lieu :
(Vehicule:2,bleu)
C’est-à-dire que je peux accéder sans restriction à l’ensemble des champs de l’objet. En effet,
j’ai en main une référence vers une table de hachage ; le fait qu’elle soit bénie ne change rien au
fait que je peux la déréférencer et accéder aux diverses valeurs qu’elle contient. Je peux aussi
bien modifier ces valeurs ou même en ajouter ou en supprimer, car il s’agit en effet d’une table
de hachage comme les autres.
≪ Comment protéger les données d’un objet ? ≫, allez-vous alors me demander. Eh bien,
Perl n’a rien prévu pour ça. Cela va sans doute faire hurler les puristes de la programmation
objet, mais c’est comme cela... Perl vous propose les principaux mécanismes pour faire de la
programmation objet tout en restant cohérent avec le reste du langage, certaines choses ne sont
donc pas possibles.
Faute de champs privés en Perl, il existe une convention qui dit que les champs dont la
clef commence par un underscore (souligné _) sont des champs privés et les scripts qui utilisent
les objets ainsi faits sont priés de respecter cette convention. C’est le cas de beaucoup de mo-
dules CPAN. Cette convention est aussi valable pour les méthodes (une méthode dont le nom
commence par un underscore est une méthode privée).
De façon générale, un programmeur Perl est quelqu’un de bonne éducation (sinon il pro-
grammerait en Java ;-))) et il ne modifiera pas une instance d’une classe qu’il n’a pas écrite.
En effet, pourquoi modifier un objet Net::FTP alors qu’il fait très bien son travail ? De toute
façon, il a forcément accès au code source de cette classe et s’il veut la modifier, il peut en faire
une copie et la modifier !
La protection des données est donc plus une nécessité sociale (manque de confiance en l’espèce
humaine à laquelle les développeurs prétendent encore faire partie :-))) qu’une nécessité tech-
nique. Pas dramatique pour faire de l’objet.
11.9 Composition
Prenons le temps de faire un petit exemple pratique pour illustrer le concept de composition.
La composition est le fait qu’un objet est constitué d’autres objets. Par exemple un garage
comporte des véhicules.
Je décide qu’un garage aura une taille limite : il s’agira du nombre maximal de véhicules
qu’il pourra contenir. Un garage devra donc contenir une liste de véhicules.
Nous devons créer un fichier Garage.pm contenant :
package Garage;
use strict;
use warnings;
# ... ici les méthodes qui vont suivre
1;
sub new {
my ($class,$places) = @_;
sub ajoute {
my ($this,$vehicule) = @_;
if( @{$this->{VEHICULE}} < $this->{PLACES} ) {
push @{$this->{VEHICULE}}, $vehicule;
return 1;
}
return 0;
}
Cette méthode prend en paramètre une référence vers un véhicule. Elle compare la longueur
de la liste des véhicules au nombre total de places (l’expression @{$this->{VEHICULE}} est la
liste pointée par la référence $this->{VEHICULE} ; évaluée en contexte numérique, elle vaut son
nombre d’éléments). S’il reste de la place, le véhicule est ajouté (fonction push) et elle renvoie
vrai, sinon elle renvoie faux.
Voici comment l’utiliser dans le script :
use Garage;
my $g = Garage->new(3);
my $v = new Vehicule( 2, "bleu" );
$g->ajoute( $v )
or die("ajoute: plus de place");
$g->ajoute( Vehicule->new( 4, "vert" ) )
or die("ajoute: plus de place");
$g->ajoute( Vehicule->new( 1, "jaune" ) )
or die("ajoute: plus de place");
sub toString {
my ($this) = @_;
my $s = "{Garage:$this->{PLACES},";
foreach my $v ( @{$this->{VEHICULE}} ) {
$s .= $v->toString();
}
return $s."}";
}
print $g->toString()."\n";
{Garage:3,(Vehicule:2,bleu)(Vehicule:4,vert)(Vehicule:1,jaune)}
On pourrait écrire cette méthode différemment, si on voulait séparer chaque véhicule par
une virgule :
sub toString {
my ($this) = @_;
my $s = "{Garage:$this->{PLACES},";
$s .= join( ’,’, map( { $_->toString() }
@{$this->{VEHICULE}} ) );
return $s."}";
}
{Garage:3,(Vehicule:2,bleu),(Vehicule:4,vert),(Vehicule:1,jaune)}
Pour ajouter encore une difficulté, je décide de trier les véhicules par nombre de roues crois-
sant :
sub toString {
my ($this) = @_;
my $s = "{Garage:$this->{PLACES},";
$s .= join( ’,’, map( {$_->toString()}
sort( {$a->{NB_ROUES} <=> $b->{NB_ROUES} }
@{$this->{VEHICULE}} ) ) );
return $s."}";
}
{Garage:3,(Vehicule:1,jaune),(Vehicule:2,bleu),(Vehicule:4,vert)}
Ces deux derniers exemples vous sont proposés pour que vous vous torturiez un peu les
méninges ;-))
$v2 = undef;
À cet instant, Perl se rend compte que l’objet en question n’est plus accessible, la mémoire
sera donc automatiquement libérée par le mécanisme du garbage collector.
La même chose a lieu dans le cas de la disparition d’une variable locale :
if( ... ) {
my $v3 = Vehicule->new(3,’jaune’);
...
...
}
La variable $v3 cesse d’exister à la fermeture de l’accolade du if. L’objet qui a été créé dans
le bloc sera donc détruit (sauf si on a fait en sorte qu’une autre variable dont la visibilité dépasse
ce bloc pointe aussi vers l’objet).
Cette libération n’est peut-être pas faite en temps réel, mais ce n’est pas au programmeur
de s’en occuper.
Il existe une méthode très spéciale, dont le nom est réservé, qui est appelée lors de la des-
truction d’une instance d’un objet. Il s’agit de la méthode DESTROY. Cette méthode sera appelée
(si elle existe dans la classe) par le garbage collector juste avant la libération de la mémoire de
l’objet.
sub DESTROY {
my ($this) = @_;
print "À la casse Vehicule ! ";
print "($this->{NB_ROUES} $this->{COULEUR})\n";
}
Cette méthode doit être définie dans le package de l’objet en question et reçoit en premier
argument une référence vers l’objet qui va être détruit.
Cette méthode est très utile pour libérer des ressources (fichier, connexion réseau, etc.) qui
ont été allouées lors de la création de l’objet.
11.11 Héritage
L’héritage est un des apports de la programmation objet. Perl dispose de tout ce qu’il faut
pour le mettre en œuvre.
Imaginons qu’en plus de véhicules, nous avons besoin de manipuler des vélos et des voitures.
Ces objets ont en commun d’avoir un nombre de roues et une couleur. Ils ont des caractéristiques
supplémentaires qui leur sont propres ; les vélos ont un nombre de vitesses et les voitures, un
nombre de sièges. Ces classes Velo et Voiture vont donc hériter de la classe Vehicule, c’est-à-
dire qu’elles vont comporter tous les champs et les méthodes de cette classe.
1;
On signale le lien de filiation entre classes au moyen de la variable @ISA positionnée à la
valeur d’une liste contenant le nom de la classe mère. ISA vient de l’anglais ”is a”, est un : on
dit qu’un vélo est un véhicule. Il hérite donc des champs et méthodes de la classe Vehicule.
Définissons-lui un constructeur :
sub new {
my ($class,$couleur,$nbVitesses) = @_;
my $this = $class->SUPER::new( 2, $couleur );
$this->{NB_VITESSES} = $nbVitesses;
return bless($this,$class);
}
Ce constructeur prend donc deux paramètres. Il appelle le constructeur de la classe mère
(syntaxe $class->SUPER::new). Il ajoute un champ NB_VITESSE et renvoie une référence bénie
en Velo. Notez bien qu’aucun appel au constructeur de la classe mère n’est fait par défaut, il
faut le faire explicitement dans tous les cas.
Le lecteur aura noté que, comme les champs de la classe mère et de la classe fille sont stockés
dans la même table de hachage, il n’y a pas de moyen simple pour faire de surcharge ou de
masquage des champs. Les noms des champs devront être minutieusement choisis pour ne pas
entrer en conflit les uns avec les autres.
Voyons à présent comment écrire une méthode pour cet objet :
sub pedale {
my ($this,$ici) = @_;
print "Sur mon vélo $this->{COULEUR} ";
print "je pédale avec $this->{NB_VITESSES} vitesses";
print " dans $ici.\n";
}
Utilisons maintenant tout cela dans notre script :
use Velo;
my $velo = Velo->new(’blanc’,18);
$velo->pedale(’les bois’);
$velo->roule(10);
Sur mon vélo blanc je pédale avec 18 vitesses dans les bois.
Avec 2 roues, je roule à 10.
Voyons à présent comment afficher un tel objet. Nous laisserons le soin à la classe Vehicule
d’afficher le vélo comme étant un véhicule et nous n’effectuerons dans la classe Velo que l’affi-
chage de ce que nous avons ajouté à la classe mère :
sub toString {
my ($this) = @_;
my $s = "[Velo:$this->{NB_VITESSES}";
$s .= $this->SUPER::toString();
$s .= "]";
}
print $velo->toString()."\n";
[Velo:18(Vehicule:2,blanc)]
sub DESTROY {
my ($this) = @_;
$this->SUPER::DESTROY();
print "Bye bye Velo ! ";
print "($this->{NB_VITESSES} $this->{NB_ROUES} ".
"$this->{COULEUR})\n";
}
package Voiture;
our @ISA = qw(Vehicule Danger Pollution);
La détermination de la bonne méthode à appeler est effectuée dynamiquement par une re-
cherche en profondeur dans l’arbre d’héritage. Je ne m’étendrai pas plus sur cette question de
l’héritage multiple.
print ref($velo)."\n";
print "Ouf, tout va bien !\n"
if( ref($velo) eq "Velo" );
Velo
Ouf, tout va bien !
Il s’agit donc de la classe ”principale” de l’objet. Sachant qu’un vélo est aussi un véhicule, il
peut être utile de pouvoir tester l’appartenance de l’objet à une classe plutôt que de connaı̂tre
sa classe principale. Par exemple, si nous manipulons un tableau comportant des objets divers
et variés et si nous souhaitons y retrouver tous les objets de classe Vehicule pour appeler leur
méthode roule, nous ne pouvons pas écrire
car les vélos ne seront pas sélectionnés. En fait la question que nous devons ici nous poser n’est
pas de savoir si la classe principale de l’objet est Vehicule, mais nous devons nous demander
si l’objet est un objet de classe Vehicule (ce qui est vrai pour tout objet de classe Vehicule
et ses sous-classes, comme Velo et Voiture). La fonction isa du package UNIVERSAL va nous
permettre de faire cela :
use UNIVERSAL;
if( UNIVERSAL::isa( $r, "Vehicule" ) ) {
$r->roule( 40 );
}
package Vehicule;
my $privateVar = 0;
our $publicVar = "hello";
package Vehicule;
my $nbVehicules = 0;
sub new {
my ($class,$nbRoues,$couleur) = @_;
my $this = {};
bless($this, $class);
$this->{NB_ROUES} = $nbRoues;
$this->{COULEUR} = $couleur;
$nbVehicules++; # un véhicule de plus
return $this;
}
À chaque appel au constructeur de cette classe, la variable $nbVehicules sera donc incrémentée.
Maintenant, comment écrire une méthode statique ? Une méthode statique (ou méthode de
classe) est une méthode qui n’est pas appelée sur une instance de la classe (donc pas de variable
$this), mais pour toute la classe. Ce n’est ni plus ni moins qu’une brave fonction présente dans
le package. Cette fonction pourra donc uniquement accéder aux champs statiques de la classe.
Nous pourrions, par exemple, écrire une méthode statique qui renvoie le nombre de véhicules
créés (variable $nbVehicules) :
sub getNbVehicules {
my ($class) = @_;
return $nbVehicules;
}
On notera que la méthode prend en premier argument le nom de la classe. Cela a pour
conséquence que l’appel à la méthode ne se fait pas tout à fait comme pour une fonction d’un
package (comme vu pour les modules), mais de la manière suivante :
print Vehicule->getNbVehicules()."\n";
Le nom de la classe est suivi d’une flèche, du nom de la méthode et des éventuels arguments
entre parenthèses. N’écrivez pas
Vehicule::getNbVehicules(), car le nom de la classe n’est pas transmis et surtout car les
mécanismes d’héritage ne sont pas mis en œuvre. S’il est possible d’écrire Velo->getNbVehicules(),
il n’est pas permis d’écrire Velo::getNbVehicules().
Le lecteur notera que les constructeurs sont des méthodes statiques. Ils retournent des
références bénies, mais n’ont rien de particulier par rapport à d’autres méthodes de classe.
Il est tout à fait possible d’appeler cette méthode sur une instance de la classe Vehicule
mais dans ce cas le premier argument reçu n’est pas le nom de la classe mais l’objet en
question (c’est donc une méthode d’instance et de classe...). Cela ne change rien pour notre
méthode getNbVehicules car elle n’utilise pas son premier argument, mais le cas est gênant pour
les constructeurs qui ont à bénir une référence. Pour cela, tout constructeur devrait commencer
par déterminer s’il a en premier argument le nom de la classe ou une référence. L’instruction qui
suit place dans la variable $class la classe actuelle, que cette variable ait pour valeur initiale le
nom de la classe ou une instance de la classe :
sub new {
my ($class,$nbRoues,$couleur) = @_;
$class = ref($class) || $class;
my $this = {};
bless($this, $class);
$this->{NB_ROUES} = $nbRoues;
$this->{COULEUR} = $couleur;
$nbVehicules++; # un véhicule de plus
return $this;
}
Le constructeur est appelé sur une instance de la classe plutôt que sur la classe. On pourrait
faire de même pour toutes les méthodes statiques (c’est même plutôt conseillé).
package Vehicule;
use strict;
use warnings;
my $nbVehicules = 0;
sub new {
my ($class,$nbRoues,$couleur) = @_;
$class = ref($class) || $class;
my $this = {};
bless($this, $class);
$this->{NB_ROUES} = $nbRoues;
Fichier Velo.pm :
package Velo;
use strict;
use warnings;
use Vehicule;
our @ISA = qw(Vehicule);
sub new {
my ($class,$couleur,$nbVitesses) = @_;
$class = ref($class) || $class;
my $this = $class->SUPER::new( 2, $couleur );
$this->{NB_VITESSES} = $nbVitesses;
return bless($this,$class);
}
sub pedale {
my ($this,$ici) = @_;
print "Sur mon vélo $this->{COULEUR} ";
print "je pédale avec $this->{NB_VITESSES} vitesses";
print " dans $ici.\n";
}
sub toString {
my ($this) = @_;
my $s = "[Velo:$this->{NB_VITESSES}";
Fichier Garage.pm :
package Garage;
use strict;
use warnings;
sub new {
my ($class,$places) = @_;
$class = ref($class) || $class;
my $this = {};
bless($this, $class);
$this->{PLACES} = $places;
$this->{VEHICULE} = [];
return $this;
}
sub ajoute {
my ($this,$vehicule) = @_;
if( @{$this->{VEHICULE}} < $this->{PLACES} ) {
push @{$this->{VEHICULE}}, $vehicule;
return 1;
}
return 0;
}
sub toString {
my ($this) = @_;
my $s = "{Garage:$this->{PLACES},";
$s .= join( ’,’, map( {$_->toString()}
sort( {$a->{NB_ROUES} <=> $b->{NB_ROUES} }
@{$this->{VEHICULE}} ) ) );
return $s."}";
}
1;
Fichier script.pl :
#!/usr/bin/perl
my $g = Garage->new(3);
$g->ajoute( $v )
or die("ajoute: plus de place");
$g->ajoute( Vehicule->new( 4, "vert" ) )
or die("ajoute: plus de place");
$g->ajoute( Vehicule->new( 1, "jaune" ) )
or die("ajoute: plus de place");
print $g->toString()."\n";
my @tab = (
Velo->new(’rose’,15),
Vehicule->new(3,’gris’),
"hello",
Velo->new(’vert’,21),
);
foreach my $r (@tab) {
if( UNIVERSAL::isa( $r, "Vehicule" ) ) {
$r->roule( 40 );
}
}
my $velo = Velo->new(’blanc’,18);
$velo->pedale(’les bois’);
$velo->roule(10);
print $velo->toString()."\n";
Nous sommes ici au terme de ce cours introductif à la programmation en Perl. Vous avez
maintenant en main la plupart des concepts et des notions pour être autonome en Perl.
N’oubliez pas que la documentation de Perl est très bien faite et est disponible sur votre
ordinateur au moyen de la commande perldoc : perldoc perl vous donne accès à la liste des
thèmes consultables avec perldoc. Par exemple perldoc perldata vous explique les structures
de données en Perl. Pour une fonction particulière, utilisez l’option -f : perldoc -f chomp Pour
un module particulier, utilisez perldoc sans option : perldoc Data::Dumper
Vous avez maintenant en main beaucoup de notions importantes de Perl. À vous de les mettre
en œuvre pour vos propres problèmes. Il n’est pas forcément facile d’exprimer son besoin en
termes directement exploitables dans un langage particulier, mais en ayant un peu d’expérience
de Perl, vous trouverez vite votre manière de résoudre un problème. Vous allez alors commencer
à faire connaissance avec Tim Towtdi (There is more than one way to do it). ;-)
127
Guide Perl https://formation-perl.fr/
129