Sommaire Carte Index Recherche Nouvelles Archives Liens A propos
[Barre Superieure]
[Barre Inferieure]
[Photo of the Author]
par Guido Socher

L´auteur:

Guido est un fan de Linux de longue date et un passionné de Perl. Ces derniers temps il est très occupé à rénover sa maison et à planter salades et autres trucs dans le jardin.

Sommaire:

Perl partie II

[Illustration]

Résumé:

Perl partie II est le premier article de notre série Perl dans lequel nous allons réellement traiter d'un programme utile. Perl partie I offrait une vue d'ensemble de Perl.



 

Un modèle pour votre programme

Perl est idéal pour écrire de petits programmes, specialisés dans une tâche. Pour accélérer le processus de développement c'est une bonne idée d'avoir un modèle sous la main qui offre une structure et des fonctionnalités de base que vous aimeriez retrouver dans la plupart des programmes. Le code suivant offre une option d'analyse basique et possède déjà une sous-routine pour afficher un message d'aide.

!/usr/bin/perl -w
# vim: set sw=8 ts=8 si et:
#
# supprimer le commentaire devant strict pour rendre
# le compilateur plus rigoureux sur les déclarations:
#use strict;
# global variables:
use vars qw($opt_h);
use Getopt::Std;
#
&getopts("h")||die "ERROR: Option inexistante. -h pour l'aide.n";
&help if ($opt_h);
#
#>>your code<<
#
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
sub help{
print "help message\n";
exit;
}
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
__END__

Observons le code. &getopts() lit les options de la ligne de commande. Il définit les variables globales du nom $opt_<option> selon les options tapées sur la ligne de commande. Chaque option commence par un "-" (signe moins) et doit suivre le nom du programme et précéder tous les autres arguments. La chaîne de caractères donnée à getopts (le "h" dans le programme ci-dessus ) liste les lettres de toutes les options autorisées. Si l'option récupère un argument il faut ajouter deux-points (:) après la lettre d'option &getsopt("d:x:h") signifie que ce programme a les options -d, -x and -h. Les options -d et -x reçoivent un argument. Ainsi "-o quelque chose" sera valide par contre "-o -x n'importe quoi" provoque une erreur puisque le -o n'est pas suivi d'un argument.
Le &help if ($opt_h); appelle la sous-routine help si l' option -h a été donnée sur la ligne de commande. Le sub help{ déclare la sous-routine. Pour l'instant, il n'est pas si important que vous compreniez chaque détail du code. Prenez-le simplement comme un modèle dans lequel vous ajouterez votre fonctionnalité principale.

 

Utilisation du modèle

Ecrivons un petit convertisseur de nombre qui utilise ce modèle. Le programme, que nous appellerons numconv, devrait convertir des nombres hexadécimaux et décimaux.
numconv -x 30 devrait afficher l'équivalent hexadécimal du décimal 30.
numconv -d 1A devrait afficher l'équivalent décimal de l'hexadécimal 1A.
numconv -h devrait afficher un texte d'aide.
La fonction perl hex() convertit des nombres hexadécimaux en décimaux et la fonction printf() peut être utilisée pour convertir des décimaux en hexadécimaux. Les insérer dans notre modèle nous donne très vite un programme sympathique:

#!/usr/bin/perl -w
# vim: set sw=8 ts=8 si et:
#
# supprimer le commentaire devant strict pour rendre
# le compilateur plus rigoureux sur les déclarations:
#use strict;
# global variables:
use vars qw($opt_d $opt_x $opt_h);
use Getopt::Std;
#
&getopts("d:x:h")||die "ERROR: Option inexistante. -h pour l'aide.n";
&help if ($opt_h);
if ($opt_d && $opt_x){
    die "ERROR:les options -x et -d s'excluent mutuellement.\n";
}
if ($opt_d){
    printf("decimal: %d\n",hex($opt_d));
}elsif ($opt_x){
    printf("hex: %X\n",$opt_x);
}else{
    # mauvaise utilisation -d ou -x doivent être précisés:
    &help;
}
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
sub help{
    print "convertit un nombre en hexadécimal ou décimal.
USAGE: numconv [-h] -d hexnum
    umconv [-h] -x decnum

OPTIONS: -h pour l'aide
EXAMPLE: numconv -d 1af
\n";
    exit;
}
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
__END__

cliquez ici pour télécharger le code du programme numconv figurant ci-dessus.
Dans les paragraphes suivants nous regarderons ce programme d'un peu plus près et nous essaierons de le comprendre.

 

Branchements conditionnels If

Le branchement if dans perl se présente sous 2 formes:
expr if (cond);
ou
if (cond) BLOCK [[elsif (cond) BLOCK ...] else BLOCK]

BLOCK est un nombre de conditions entre crochets {}. Cela signifie que vous pouvez par exemple écrire:

printf("hello\n") if ($i);

if ($i == 2){
   printf("i is 2\n");
}elsif ($i == 4){
   printf("i is 4\n");
}else{
   printf("i is neither 2 nor 4\n");
}

Comme en C, il est également possible d'utiliser les opérateurs && et ||.
printf("hello\n") if ($i);
Peut par conséquent être écrit sous la forme
($i) && printf("hello\n");
Le || tel qu'utilisé dans notre modèle traduit parfaitement les mots en langage parlé.
&getopts("d:x:h")||die "ERROR\n";
"Get the options or die". La fonction die() est équivalente à un printf suivi d'exit. Ainsi, il affiche un message et termine le programme.
&getopts("d:x:h")||die "ERROR\n";
est équivalent à
die "ERROR\n"; if (! &getopts("d:x:h"));
Où le ! est un opérateur logique not. Encore une fois ceci peut ausi être écrit sous la forme
die "ERROR\n"; unless (&getopts("d:x:h"));
unless est équivalent à if-not et il est plus élégant à lire que if(!..)

 

Variables

Dans le premier article sur perl nous avons vu que les variables scalaires (les $-variables) étaient utilisées sans les déclarer. Elles se crééent au moment d'être utilisées. C'est une caractéristique intéressante pour de petits programmes mais qui peut entraîner des erreurs difficiles à trouver dans des programmes plus longs. Déclarer une variable donne au compilateur la possibilité de faire des vérifications complémentaires sur les fautes de frappe.
"use strict;" vous force à tout déclarer.
Considérons le code suivant.

#!/usr/bin/perl
#
$i=1;
print "i is $tpyerr\n";

Ce code fonctionnera parfaitement dans perl et donnera "i is ". Le module perl "use strict;" peut forcer le compilateur à se plaindre d'un tel programme. Lorsque vous utilisez "strict" tout doit être déclaré sinon c'est une erreur.

#!/usr/bin/perl
use strict;
my $i=1;
print "i is $tpyerr\n";

Ceci génère le message suivant et rend la faute de frappe facile à trouver

Global symbol "$tpyerr" requires explicit package name at ./vardec line 4.
Execution of ./vardec.txt aborted due to compilation errors.
Exit 255

Maintenant il est facile de corriger le code du programme:

#!/usr/bin/perl
use strict;
my $i=1;
print "i is $i\n";

Les variables dans perl peuvent être déclarées en utilisant "my" ou, comme nous l'avons déjà vu dans le modèle, "use vars":
use vars qw($opt_h);

Les variables globales sont déclarées par use vars. Ces variables sont globales pour toutes les bibliothèques incluses.
Les variables locales au fichier du programme en cours (globales parmi toutes les sous-routines de ce fichier) sont déclarées par my au début du programme (en dehors d'une sous-routine).
Les variables locales à la sous-routine en cours sont déclarées par my à l'intérieur de la sous-routine.

Les personnes expérimentées en programmation shell peuvent être tentées de laisser de côté le signe $ en déclarant la variable ou en lui attribuant une valeur. Ce n'est pas possible dans perl. Vous devez toujours écrire un signe $ lorsque vous utilisez une variable scalaire quoi que vous fassiez avec.

Vous pouvez aussi attribuer directement une valeur à la variable lorsque vous la déclarez. my $myvar=10; déclare la variable $myvar et définit sa valeur initiale à 10.

 

Sous-routines

Nous avons déjà utilisé la sous-routine "help" dans le programme numconv ci-dessus. Les sous-routines peuvent être utilisées pour programmer des fonctions personnalisées. Elles aident à structurer votre programme.
Une sous-routine peut être incluse n'importe où dans le texte du programme (avant ou après qu'elle soit appelée. Ca n'a pas d'importance). Vous lancez une sous-routine par sub name(){... et vous l'appelez par $retval=&name(...arguments...). La valeur retournée est la valeur de la dernière commande exécutée dans la sous-routine. Les arguments donnés à la sous-routine sont passés au code à l'intérieur de la sous-routine dans le tableau réservé @_. Nous verrons cela plus en détail lorsque nous aborderons les tableaux de variables. Pour l'instant, il suffit de savoir que les valeurs des variables scalaires peuvent être lues dans la sous-routine en utilisant shift. Voici un exemple:

#!/usr/bin/perl
use strict;
my $result;
my $b;
my $a;
$result=&add_and_duplicate(2,3);
print "2*(2+3) is $result\n";

$b=5;$a=10;
$result=&add_and_duplicate($a,$b);
print "2*($a+$b) is $result\n";

# additionner deux nombres et les multiplier par 2:
sub add_and_duplicate(){
    my $locala=shift;
    my $localb=shift;
    ($localb+$locala)*2;
}
 

Un vrai programme

Maintenant que nous avons traité une grosse partie de la syntaxe de perl et des éléments du langage, il est temps d'écrire un vrai programme.
Perl a été conçu pour manipuler des fichiers texte avec très peu d'effort de programmation. Notre premier programme Perl doit comparer une liste d'abréviations et trouver les doublons dans cette liste. Par doublons nous entendons abréviations apparaissant plusieurs fois dans la liste. La liste ressemble à ce qui suit:

Il est facile de manipuler des fichiers texte avec Perl
AC Access Class
AC Air Conditioning
AFC Automatic Frequency Control
AFS Andrew File System
...

Vous pouvez télécharger la liste ici. La syntaxe de ce fichier est la suivante:

Comment lire un tel fichier texte? Voici une partie du code perl pour lire le texte ligne par ligne:


....
open(FD,"abb.txt")||die "ERROR: can not read file abb.txt\n";
while(){
   #do something
}
close FD;
....

La fonction open prend un descripteur de fichier comme premier argument et le nom du fichier à lire comme second argument. Les descripteurs de fichiers sont une sorte de variables particulières. Il n'est pas nécessaire de comprendre ce qu'est réellement un descripteur de fichier. Vous le mettez simplement dans la fonction open, vous l'utilisez dans la fonction qui lit les données du fichier et pour finir vous le renvoyez à la fonction close. La lecture du fichier est faite par <FD>. Les <FD> peuvent être passés comme argument à une boucle while et le résultat est une lecture ligne par ligne.
Traditionnellement les descripteurs de fichiers sont écrits en majuscules dans Perl.

Où vont nos données? Perl possède un certain nombre de variables implicites. Ce sont des variables que vous n'avez pas déclarées. Elles sont toujours présentes. Une telle variable est $_. Cette variable contient la ligne lue à ce moment par la boucle while mentionnée ci-dessus. Essayons (télécharger le code):

#!/usr/bin/perl
use strict;
my $i=0;
open(FD,"abb.txt")||die "ERROR: can not read file abb.txt\n";
while(<FD>){
   # incrémente le compteur de ligne. Vous connaissez
   # sans doute le ++ du C:
   $i++;
   print "Line $i is $_";
}
close FD;
La variable implicite $_ contient la ligne courante.

Vous remarquerez que nous n'avons PAS écrit print "Line $i is $_ \n". La variable $_ contient la ligne en cours du fichier texte ainsi que le caractère nouvelle ligne (\n).

Maintenant, nous savons comment lire le fichier. Pour terminer vraiment notre programme nous devons apprendre encore 2 choses:

  1. Comment lire l'abréviation depuis le début de la ligne.
  2. Comment fonctionnent les tables de hachage dans perl

Expressions régulières offre un moyen sophistiqué de rechercher un modèle dans une chaîne de texte. Nous recherchons la première chaîne d'une ligne jusqu'au premier espace. En d'autres termes notre modèle correspond à "début de ligne-->un nombre de caractères sans espace-->un espace". Traduit en expressions régulières de perl ça nous donne ^\S+\s. Si nous intégrons cela dans un m//; perl appliquera cette expression à la variable $_ (Rappel: cette variable contient la ligne courante; ça c'est bien). Le \S+ dans les expressions régulières correspond à " un nombre de caractères sans espace". Si nous mettons le \S+ entre parenthèses nous récupérons le "caractère sans espace" dans la variable $1. Nous pouvons ajouter cela dans notre programme:

#!/usr/bin/perl -w
# vim: set sw=8 ts=8 si et:
#
use strict;
# global variables:
use vars qw($opt_h);
my $i=0;
use Getopt::Std;
#
&getopts("h")||die "ERROR: No such option. -h for help.n";
&help if ($opt_h);
#
open(FD,"abb.txt")||die "ERROR: can not read file abb.txt\n";
while(<FD>){
    $i++;
    if (m/^(\S+)\s/){
        # $1 contient maintenant le premier mot (\S+)
        print "$1 est l' abréviation de la ligne $i\n";
    }else{
        print "Ligne $i ne commence pas par une abréviation\n";
    }
}
close FD;
#
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
sub help{
     print "help text\n";
     exit;
}
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
__END__

L'opérateur match (m/ /) retourne 1 si l'expression régulière a été correctement appliquée à la ligne courante. Nous pouvons donc l'utiliser dans un branchement if. Vous devriez toujours utiliser un branchement if avant $1 (or $2, $3 ...) pour vous assurer que $1 contient vraiment des données valides.

   

Tables de hachage

Nous pouvons maintenant lire le fichier et obtenir l'abréviation, tout ce qui manque c'est un moyen de savoir si nous avons déjà lu cette abréviation. Ici nous avons besoin d'un nouveau type de données perl: Les Tables de hachage. Les tables de hachage sont des tableaux de variables qui peuvent être indexés par une chaîne de caractères. Lorsque vous voulez la table de hachage complète vous écrivez dans perl un caractère % devant le nom de la variable. Pour obtenir une seule valeur vous utilisez $variable_name{"index_string"}. Nous utilisons le même $ que pour les autres variables scalaires puisqu'un champ de la table de hachage n'est autre qu'une variable scalaire normale. Voici un exemple:

#!/usr/bin/perl -w
my %htab;
my $index;
# Chargement du tableau avec les données:
$htab{"something"}="valeur de something";
$htab{"somethingelse"}=42;
# Récupération des données:
$index="something";
print "%htab at index \"$index\" is $htab{$index}\n";
$index="somethingelse";
print "%htab at index \"$index\" is $htab{$index}\n";

A l'exécution du programme nous obtenons:

%htab at index "something" est la valeur de something
%htab at index "somethingelse" est 42

Maintenant notre programme est complet:

 1  #!/usr/bin/perl -w
 2  # vim: set sw=4 ts=4 si et:
 3  # 
 4  use strict;
 5  # global variables:
 6  use vars qw($opt_h);
 7  my %htab;
 8  use Getopt::Std;
 9  #
10  &getopts("h")||die "ERROR: No such option. -h for help.n";
11  &help if ($opt_h);
12  #
13  open(FD,"abb.txt")||die "ERROR: can not read file abb.txt\n"; 
14  print "Abbreviations with several meanings in file abb.txt:\n";
15  while(<FD>){ 
16      if (m/^(\S+)\s/){
17          # we use the first word as index to the hash:
18          if ($htab{$1}){
19              # again this abbrev:
20              if ($htab{$1} eq "_repeated_"){
21                  print; # same as print "$_";
22              }else{
23                  # this is the first duplicate we print first
24                  # occurance of this abbreviation:
25                  print $htab{$1};
26                  # print the abbreviation line that we are currently reading:
27                  print;
28                  # mark as repeated (= appears at least twice)
29                  $htab{$1}="_repeated_";
30              }
31          }else{
32              # the first time we load the whole line:
33              $htab{$1}=$_;
34          }
35      }
36  } 
37  close FD; 
38  #
39  #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
40  sub help{
41          print "finddup -- Find abbreviations with several meanins in the
42  file abb.txt. The lines in this file must have the format:
43  abrev meaning
44  \n";
45          exit;
46  }
47  #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
48  __END__ 
Vous pouvez télécharger le programme en cliquant ici.

Comment fonctionne-t-il? Nous lisons le fichier ligne par ligne et nous stockons les lignes dans notre table de hachage nommée %htab (ligne 33). L'index pour le hachage est l'abréviation. Avant de charger la table de hachage nous vérifions qu'il n'y a pas déjà quelque chose de stocké dedans (ligne 18). S'il y a déjà quelque chose dans la table nous avons 2 possibilités:

  1. C'est le premier doublon
  2. Nous avions déjà plusieurs doublons de cette abréviation
Pour différencier les 2 cas nous écrivons la chaîne "_repeated_" dans la table de hachage pour marquer que nous avons déjà trouvé un doublon dans le fichier (ligne 29).

Il est probablement mieux de télécharger le code et de le tester.

 

Et après?

Dans cet article vous avez déjà appris quelques détails du langage perl. Nous n'avons pas encore abordé tous les types de données de perl et vous vous demandez sans doute également s'il est possible de ne pas coder en dur le nom de fichier "abb.txt" dans notre programme ci-dessus. Vous savez déjà comment utiliser une option pour éviter cela (e.g finddup -f abb.txt). Essayez de modifier le programme! La manière générale de lire la ligne de commande dans perl sera traitée dans le prochain article.
Site Web maintenu par l´équipe d´édition LinuxFocus
© Guido Socher
LinuxFocus 1999
Translation information:
en -> -- Guido Socher
en -> fr Georges Tarbouriech

1999-11-12, generated by lfparser version 0.7