Les fichiers.

Rappels.

Nous avons déjà abordé ce problème. nous alllons y revenir et détailler un peu plus certaines particularités.
Rappelons l'existence de l'opérateur diamand qui a souvent été utilisé pour des programmes de démonstration.
Le nom du fichier concerné doit être stocké dans la liste spéciale prédéfinie @ARGV.
Cette opération pouvant se faire au moyen d'une affectation.

@ARGV = ("monfichier.txt","donnees.txt","valeurs.txt");

0u bien en utilisant la liste d'appel de la ligne de commande.
Si la liste contient plusieurs fichiers, on accèdera ligne par ligne au premier,puis on passera au suivant et ainsi de suite jusqu'à épuisement de la liste.
Le descripteur de l'opérateur diamand est <> (d'où son nom)

Programme fich1.pl Exécution sur l'écran
@ARGV = ("entree.txt");
while ($ligne = <>){
  print("$ligne");
}
    
c:\progs> perl fich1.plent
Ligne 1 du fichier entree.txt
Ligne 2 du fichier entree.txt
Ligne 3 du fichier entree.txt
Ligne 4 du fichier entree.txt
Ligne 5 du fichier entree.txt
Ligne 6 du fichier entree.txt
Ligne 7 du fichier entree.txt
Ligne 8 du fichier entree.txt
Ligne 9 du fichier entree.txt
c:\progs>
    
Programme fich2.pl Exécution sur l'écran
@ARGV = ("e1.txt","e2.txt");
while ($ligne = <>){
  print("$ligne");
}
    
c:\progs> perl fich2.plent
Ligne 1 du fichier e1.txt
Ligne 2 du fichier e1.txt
Ligne 3 du fichier e1.txt
Ligne 4 du fichier e1.txt
Ligne 5 du fichier e1.txt
Ligne 1 du fichier e2.txt
Ligne 2 du fichier e2.txt
Ligne 3 du fichier e2.txt
Ligne 4 du fichier e2.txt
Ligne 5 du fichier e2.txt
c:\progs>
    

Par ailleurs, l'utilisation de la variable standard d'entrée permet de simplifier à l'extrème l'accés à un fichier.

Programme fich3.pl Exécution sur l'écran
while (<>){
  print;
}
    
c:\progs> perl fich3.pl entree.txtent
Ligne 1 du fichier entree.txt
Ligne 2 du fichier entree.txt
Ligne 3 du fichier entree.txt
Ligne 4 du fichier entree.txt
Ligne 5 du fichier entree.txt
Ligne 6 du fichier entree.txt
Ligne 7 du fichier entree.txt
Ligne 8 du fichier entree.txt
Ligne 9 du fichier entree.txt
c:\progs>
    

L'accés simultané à plusieurs fichiers en lecture ou/et en écriture, nécessite de passer par des descripteurs explicites.

L'opérateur diamand est extrèmement utile lorsque les données sont à lire sur un seul fichier et sont exclusivement accessibles en lecture.
Pour pouvoir accéder simultanément à plusieurs fichiers en lecture ou/et en écriture, il est nécessaire se passer par des descripteurs explicites.

Opération.Instruction.
Ouverture d'un fichier en lecture. open (DESC, "nom");
Ouverture d'un fichier en écriture. open (DESC, "nom");
Ouverture d'un fichier en écriture incrémentale open (DESC, ">>nom");
Ouverture d'un fichier en lecture / écriture. open (DESC, "+>nom");
Fermeture d'un fichier. close (DESC);

DESC est le nom du descripteur qui va désormais permettre d'accéder au fichier en question.
"nom" est la chaîne de caractères indiquant le nom du fichier.
Lorsqu'on ouvre un fichier en écriture, le fichier, qu'il existe ou nom, est détruit et l'écriture commence au début.
Lorsqu'on ouvre un fichier en lecture incrémentale, les écritures commencent à la fin du fichier existant. toutes les informations qu'il contient sont ainsi conservées.
Exemples :

ProgrammeCommentaire
open (ENTREE, "donnees.txt");
while(<ENTREE>){
print;
}
close(ENTREE);
Ouverture du fichier "donnees.txt" en lecture et accés séquentiel à ses informations.
open (SORTIE, "resultats.txt");
for ($i=1;$i<=100;$i++) {
print SORTIE ("$i/n");
}
close(SORTIE);
Ouverture du fichier "résultat.txt" en écriture et écriture séquentielle. A la fin, le fichier contiendra les 100 premiers entiers à raison de 1 par ligne.

La fonction die().

C'est une fonction qui va permettre la récupération d'une erreur sur un fichier et la transmission d'un message d'alerte.
Savoir si l'opération demandée sur un fichier a été couronnée de succés pourrait se faire de la manière suivante :

unless ( open (FICHIER, "/tmp/fichier")) {
  print "Desole, impossible d'acceder a /tmp/fichier.\n";
} else {
  # Deroulement normal du programme.
}
  

L'ouverture renvoyant la valeur 'vrai' si l'opération s'est bien passée, 'faux' dans le cas contraire.
Perl propose, pour réaliser cette opération, une fonction plus pratique d'utilisation, la fonction die().

unless ( open (FICHIER, "/tmp/fichier")) {
  die "Desole, impossible d'acceder a /tmp/fichier.\n";
}
# Deroulement normal du programme.
  

Ou plus simplement en utilisant l'évaluation en court circuit des expressions logiques.

open (FICHIER, "/tmp/fichier") or
  die "Desole, impossible d'acceder a /tmp/fichier.\n"
  

Une phrase de L. Wall résumede manière quelque peu provocatrice l'action de la commande die.

"Open that file or die "

La commande die possède une particularité intéressante.
La chaîne de caractères qui lui est soumise pour impression peut se terminer ou ne pas se terminer par un \n.
C'est ce caractère qui influera sur le contenu du message qui sera finalement affiché.
Si le \n n'est pas présent à la fin de la chaîne, un message indiquant le nom du programme Perl qui a généré l'erreur et le numéro de la ligne contenant le die incriminé sera affiché à la suite de la chaîne en question.

Programme die1.pl Exécution sur l'écran
open (FICH,"toto") or
  die "erreur d\'ouverture.";
    
c:\progs> perl die1.plent
# erreur d'ouverture.
File 'die1.pl'; Line 1
c:\progs>
    

Dans le cas contraire, seul le message demandé sera présenté.

Programme die1.pl Exécution sur l'écran
open (FICH,"toto") or
  die "erreur d\'ouverture.\n";
    
c:\progs> perl die1.plent
# erreur d'ouverture.
c:\progs>
    

Nous détaillerons ultérieurement d'autres particularité de cette fonction qui permet de terminer un programme en imprimant un message d'erreur.

La fonction select().

C'est une fonction qui va permettre de spécifier la sortie par défaut lors d'une opération d'entrée sortie.

Programme fich4.pl Exécution sur l'écran
#!/usr/bin/perl
open (FICHIER,">Sortie.txt");
$chaine = "Perl est vraiment un super langage.";
&ecrire_une_ligne;
select (FICHIER);
&ecrire_une_ligne;
close (FICHIER);

# Declaration procedure.
sub ecrire_une_ligne {
  print ("J'écris :\n");
  print ("$chaine\n");
  print (" sur le fichier de sortie.\n");
}
    
c:\progs> perl fich4.plent
J'écris :
Perl est vraiment un super langage.
sur le fichier de sortie.
c:\progs>
    

Regardons maintenant le contenu di fichier "Sortie.txt"

Programme fich4bis.pl Exécution sur l'écran
@ARGV = ("Sortie.txt");
while (<>){
  print;
}
    
c:\progs> perl fich4bis.plent 
J'écris :
Perl est vraiment un super langage.
sur le fichier de sortie
c:\progs>
    

Explications :
La procédure écrit une ligne sur le périphérique standard de sortie <STDOUT>.
Avant l'instruction "select (FICHIER);", le message apparait bien sur l'écran qui est toujours le périphérique de sortie standard.
Par contre, l'exécution de cette instruction effectue une redirection de <STDOUT> vers le fichier explicitement spécifié. La seconde écriture se fait sur le fichier en question.
Utilisé entant que fonction "$s = select (FICHIER);" elle permet de récupérer dans la variable le descripteur qui vient d'être remplacé.

    select (SORTIE);
    . . . .
    my $ancien_desc = select (FICHIER);

Permet de récupérer dans le scalaire "$ancien_desc" le descripteur vers lequel était précédemment redirrigé la sortie tout en procédant à une nouvelle redirection.
Il sera ainsi possible au moyen de l'instruction

select ($ancien_desc);

De revenir en arrière.

Les redirections.

Il est aussi possible de redirriger tous les fichiers standards. Ainsi, la rédéfinition des descripteurs <STDIN>, <STDOUT> et <STDERR> est parfaitement licite.

open (STDOUT,">sortie.txt");

Permet d'effectuer une redirection du descripteur vers le fichier spécifié.
Toutefois, selon les systèmes utilisés, les redirections peuvent reserver quelques surprises.
Prenons par exemple le programme suivant :

#!usr/bin/perl
open (STDOUT,">sortie.txt") || die ("Pas moyend'ouvrir STDIN");
open (STDERR,">STDOUT") || die ("Pas moyen d'ouvrir STDERR");
print STDOUT ("La première ligne, va sur la sortie (STDOUT).\n");
print STDERR ("La seconde ligne, va sur la sortie erreur(STDERR).\n");
close (STDOUT);
close (STDERR);
  

Il redirrige simultanément la sortie <STDOUT> vers un fichier (sortie.txt) et la sortie <STDERR> vers <STDOUT>, soit le même fichier.
Selon qu'on l'exécute sous un système Unix ou sous unautre système, les résultats seront quelque peu différents.
Sous certains systèmes d'exploitation, le fichier sortie.txt contient :

La première ligne, va sur la sortie (STDOUT).
La seconde ligne, va sur la sortie erreur(STDERR).
  

Ce qui n'est pas trop surprenant.
Sous Unix, le fichier sortie.txt contient :

La seconde ligne, va sur la sortie erreur(STDERR).
La première ligne, va sur la sortie (STDOUT).
  

Et ca, c'est réellement une surprise qui mérite une explication.
Manifestement, la première instruction redirige la sortie standard (<STDOUT>) vers le fichier que nous avons apelé sortie.txt.
De manière tout aussi évidente, la seconde instruction redirige la sortie erreur (<STDERR>) vers la sortie standard (<STDOUT>) soit en définitive vers la fichier sortie.txt.
L'inversion des lignes vient de la manière dont unix gére ses accés.
L'accés à un fichier, sortie.txt par exemple, se fait par l'intermédiaire d'un buffer, et la copie physique sur le fichier n'a lieu que lorsque le buffer est plein ou lorsque le programme se termine.
Bien qu'ils soient en fin de compte redirrigés vers la même destination, les buffers que le système utilise pour <STDOUT> et <STDERR> sont physiquement différents.
La première ligne est stockée dans la zone correspondant à <STDOUT<> la alors que la seconde ligne est stockée dans celle correspondant à <STDERR>.
Ce n'est qu'à la fin du programme qu'ils seront recopiés dans leur destination finale, le fichier sortie.txt.
Et c'est là que se produit l'inversion car Unix commence toujours par recopier le buffer correspondant à <STDERR> avant de procéder à la même opération pour pour celui correspondant à <STDOUT>.

La non bufferisation.

Le seul moyen de s'affranchir de ce problème consiste à supprimer la bufferisation des sorties.
Nous disposons pour celà d'une variable prédéfinie (une de plus) à laquelle doit être assignée une valeur différente de zéro si on désire supprimer la bufferisation desdonnées.
Son action porte sur <STDOUT> ou sur le dernier fichier spécifié par une sélection (select).
Le nom de cette variable est $|.

#!usr/bin/perl
open (STDOUT,">sortie.txt") || die ("Pas moyen d'ouvrirSTDIN");
open (STDERR,">&STDOUT") || die ("Pas moyend'ouvrir STDERR");
select (STDERR);
# Suppression de la bufferisation de STDERR.
$| = 1;
select (STDOUT);
# Suppression de la bufferisation de STDOUT.
$| = 1;
print STDOUT ("La première ligne, va sur la sortie (STDOUT).\n");
print STDERR ("La seconde ligne, va sur la sortie erreur(STDERR).\n");
close (STDOUT);
close (STDERR);
close (STDERR);
  

Dans ces conditions, une exécution sous Unix, stockera dans le fichier sortie.txt :

La première ligne, va sur la sortie (STDOUT).
La seconde ligne, va sur la sortie erreur(STDERR).
  

Purge d'un descripteur.

Nous verrons plus loin, lorsqu'il sera question de communication entre les processus, qu'il est parfois nécessaire de purger un descripteur.
La fonction select permet d'effectuer simplement cette opération.

my $ancien = select (FICHIER); $| =1; select ($ancien);
my $ancien = select (SORTIE); $| =1; select ($ancien);
  

Permet d'effectuer la purge des deux buffers liés aux descripteurs <FICHIER> et <SORTIE>.

La fonction eof.

Cette fonction retournera une valeur différente de Zéro lorsque la fin de fichier aura été atteinte.

Programme fich5.pl Exécution sur l'écran
#!/usr/bin/perl
@ARGV = ("Fichier1.txt", "Fichier2.txt");
while ($ligne = <>) {
  print ("$ligne");
  if (eof) {
    print ("\n -- Fin du fichier courant --\n");
  }
}
    
c:\progs> perl fich5.plent
Ligne 1 du fichier 1.
Ligne 2 du fichier 1.
Ligne 3 du fichier 1.
Ligne 4 du fichier 1.
Ligne 5 du fichier 1.
Ligne 6 du fichier 1.
Ligne 7 du fichier 1.
Ligne 8 du fichier 1.
Ligne 9 du fichier 1.

-- Fin du fichier courant --

Ligne 1 du fichier 2.
Ligne 2 du fichier 2.
Ligne 3 du fichier 2.
Ligne 4 du fichier 2.
Ligne 5 du fichier 2.
Ligne 6 du fichier 2.
Ligne 7 du fichier 2.
Ligne 8 du fichier 2.
Ligne 9 du fichier 2.

-- Fin du fichier courant --
c:\progs>
    

Il existe deux fonctions eof et eof().
Nous avons vu ci dessus la fonction eof, voyons maintenant un exemple de eof().

Programme fich6.pl Exécution sur l'écran
#!/usr/bin/perl
@ARGV = ("Fichier1.txt", "Fichier2.txt");
while ($ligne = <>) {
  print ("$ligne");
  if (eof()) {
    print ("\n -- Fin du fichier courant --\n");
  }
}
    
c:\progs> perl fich6.plent
Ligne 1 du fichier 1.
Ligne 2 du fichier 1.
Ligne 3 du fichier 1.
Ligne 4 du fichier 1.
Ligne 5 du fichier 1.
Ligne 6 du fichier 1.
Ligne 7 du fichier 1.
Ligne 8 du fichier 1.
Ligne 9 du fichier 1.

Ligne 1 du fichier 2.
Ligne 2 du fichier 2.
Ligne 3 du fichier 2.
Ligne 4 du fichier 2.
Ligne 5 du fichier 2.
Ligne 6 du fichier 2.
Ligne 7 du fichier 2.
Ligne 8 du fichier 2.
Ligne 9 du fichier 2.

-- Fin du fichier courant --
c:\progs>
    

Explication :
La fonction "eof" passe à vrai à la fin de chacun des fichiers de la liste d'appel (@ARGV), alors que la fonction "eof()" ne passe a vrai qu'une fois et une seule à l'épuisement de la liste d'appel.

Passage de noms de fichiers.

Il est possible de passer les noms de fichiers en tant que paramètres.

Programme fich7.pl Exécution sur l'écran
#!/usr/bin/perl   
sub ouvrir_fichier {
  my ($ref, $sens, $nom) = @_;
  open ($ref, $sens.$nom) or
    die ("Erreur ouverture $nom\n")
}

sub lire_fichier {
  my ($ref) = @_;
  <$ref>;
}

sub ecrire_fichier {
  my ($ref, $ligne) = @_;
  print $ref ($ligne);
}

print ("Nom du fichier à lire ?\n");
$nom_fichier = <STDIN>;
&ouvrir_fichier ("ENTREE", "", $nom_fichier);
print("Nom du fichier aeécrire ?\n");
$nom_du_fichier = <STDIN>;
&ouvrir_un_fichier ("SORTIE", ">",$nom_fichier);
$num = 1;
while ($ligne = &lire_fichier("ENTREE")) {
  print ("Traitement de la ligne $num.\n");
  $num++;
  &ecrire_fichier("SORTIE", $ligne);
}
print("\nFin du programme.\n");
    
c:\progs> perl fich7.plent
Nom du fichier à lire ?
Poeme.txtent
Nom du fichier a ecrire ?
Sortie.txtent
Traitement de la ligne 1.
Traitement de la ligne 2.
Traitement de la ligne 3.
Traitement de la ligne 4.
Traitement de la ligne 5.
Traitement de la ligne 6.
Traitement de la ligne 7.
Traitement de la ligne 8.
Traitement de la ligne 9.
Traitement de la ligne 10.
Traitement de la ligne 11.
Traitement de la ligne 12.
Traitement de la ligne 13.
Traitement de la ligne 14.
Traitement de la ligne 15.
Traitement de la ligne 16.
Traitement de la ligne 17.

Fin du programme.

c:\progs>
    

Les fonctions seek() et tell().

Une autre particularité de Perl est la possibilité qu'il offre de pouvoir sauter des information ou bien de procéder à leur relecture.
Les deux fonctions seek() et tell () sont à notre disposition pour effectuer ces opérations.
La fonction seek va permettre de sauter des caractères d'un fichier donné.
La fonction tell va permettre de récuperer la distance (en caractères qui sépare le début du fichier de la position courante dans le fichier.
La position courante est l'emplacement de la ligne qui est sur le point d'être lue.

La syntaxe de la fonction seek est la suivante :

seek (référence, distance, relativement_a);
référence Référence du fichier concerné.
distance Entier représentant le nombre de caractères à sauter
relativement_a Entier égal à 0, 1 ou 2.
0 : relativement au début du fichier.
1 : relativement à la prochaine ligne qui doit être lue.
2 : relativement à la fin du fichier.

Ainsi :

L'instruction. Provoque.
seek (REF,0,0) Un retour au début du fichier.
seek (REF,100,1) Un saut de 100 caractères en avant.
seek (REF,-100,1) Un saut de 100 caractères en arrière.
seek (REF,0,2) Un positionnement en fin de fichier.

La syntaxe de la fonction tell est la suivante :

tell (référence);
référence Référence du fichier concerné.

Attention, seek et tell ne peuvent en aucun cas être utilisés sur des fichiers qui font référence à des tubes.

c:\progs> type fich8.plent
#!/usr/bin/perl
@liste_de_chiffres = ("zéro","un","deux","trois","quatre",
"cinq","six","sept","huit","neuf");
# Creation du fichier.
open (FICHIER, ">Sortie.txt");
foreach $chiffre (@liste_de_chiffres) {
  print FICHIER ("$chiffre.\n");
}
close (FICHIER);
open (FICHIER, "Sortie.txt");
while (1) {
  $memorise = tell (FICHIER);
  $ligne= <FICHIER>;
  last if ($ligne eq "");
  print ("Lecontenu de la ligne est : $ligne");
  $ligne = <FICHIER>;
  print ("Le contenu de la ligne est : $ligne");
  print ("-retour à la position mémorisée ($memorise).\n");
  seek (FICHIER,$memorise_la_position, 0);
  $ligne = <FICHIER>;
  print ("Lecontenu de la ligne est : $ligne");
  $ligne = <FICHIER>;
  print ("Le contenu de la ligne est : $ligne");
}
c:\progs>perl fich8.plent
Le contenu de la ligne est : zéro.
Le contenu de la ligne est : un.
- retour à la position mémorisée (0).
Le contenu de la ligne est : zéro.
Le contenu de la ligne est : un.
Le contenu de la ligne est : deux.
Le contenu de la ligne est : trois.
- retour à la position mémorisée (10).
Le contenu de la ligne est : deux.
Le contenu de la ligne est : trois.
Le contenu de la ligne est : quatre.
Le contenu de la ligne est : cinq.
- retour à la position mémorisée (23).
Le contenu de la ligne est : quatre.
Le contenu de la ligne est : cinq.
Le contenu de la ligne est : six.
Le contenu de la ligne est : sept.
- retour à la position mémorisée (37).
Le contenu de la ligne est : six.
Le contenu de la ligne est : sept.
Le contenu de la ligne est : huit.
Le contenu de la ligne est : neuf.
- retour à la position mémorisée (48).
Le contenu de la ligne est : huit.
Le contenu de la ligne est : neuf.
c:\progs>
  

Précédent
Suivant