wiki:InsiaProgPerlFile

Sommaire

Programmation Perl - Utilisation des fichiers

1. Descripteurs

Pour manipuler un fichier, il faut d'abord obtenir une "poignée" ou "descripteur" (handle) permettant de le saisir.

Syntaxes

Perl peut également manipuler ces descripteurs avec deux types de syntaxe. La syntaxe "ancienne" ou traditionnelle utilise des noms en majuscules avec uniquement des lettres, et ne sont jamais déclarés et assignés. La portée de ces descripteurs est globale:

open(TEXT, ">text");
print TEXT "Bonjour le monde.\n";
close(TEXT);

La deuxième syntaxe, plus "attendue" permet d'utiliser une variable standard et donc locale (il s'agit en fait d'une référence vers un descripteur):

open(my $text, ">text");
print $text "Bonjour le monde.\n";
close($text);

Notons qu'il existe une version moderne (orienté objet) qui requiert l'utilisation d'un module spécifique (cf man filehandle):

use FileHandle;

$text = new FileHandle(">text");
print $text "Bonjour le monde.\n";
$text->close();

Il est aussi possible de séparer le mode d'ouverture et le nom du fichier, ce qui permet de se prémunir de l'interaction hasardeuse avec la présence d'éventuels caractères interprétés par Perl dans le nom des fichiers (solution donc nettement plus robuste et sécurisée):

open(my $text, ">", "text");

Descripteurs implicites

Perl utilise plusieurs descripteurs implicites par défaut, qui peuvent être nommés:

  • STDOUT: l'entrée standard, qui est naturellement utilisée par print et consorts
  • STDERR: la sortie d'erreur, utilisée notamment par die
  • STDIN: l'entrée standard, utilisée par défaut par l'opérateur de lecture <>

2. Ouverture

Via le shell

La façon la plus économique d'ouvrir un fichier et de ne pas le faire, ou plus simplement de déléguer cette tâche au shell. Ainsi il suffit d'appeler l'interpréteur Perl en redirigeant les fichiers ou programmes de son choix sur l'entrée ou la sortie standard:

$ ./test.pl <input 2>error.log |grep qqchose

Mode simple

On utilise la fonction open, qui accepte comme nom de fichier une syntaxe ressemblant au shell et permettant de désigner les différents scenarii d'ouverture:

open($file, "test") Lecture seule
open($file, "<test") Lecture seule
open($file, ">test") Ecriture avec troncature
open($file, ">>test") Ecriture en fin de fichier
open($file, "+<test") Lecture/écriture (fichier existant, sans troncature)
open($file, "+>test") Lecture/écriture (troncature, création de fichier si nécessaire)
open($file, "+>>test") Lecture/écriture (sans troncature, écriture depuis al fin, création de fichier si nécessaire)

Avec une variable

Il est important de noter qu'avec l'utilisation de variables classiques Perl, un fichier est automatiquement fermé dès que l'on quitte la portée de la variable:

sub log_error {
  open(my $log, ">>error.log") or die;
  print $log @_;
}

Avec des programmes

On peut facilement reproduire le mécanisme du "pipe" utilisé dans le shell, c'est-à-dire relier sur l'entrée ou la sortie standard un autre programme:

open(my $mail, "|sendmail -bi") or die;
print $mail "From: eleve@insia.org\r\nSubject: test\r\n";
close $mail;
open(my $log, "gzip mail.1.gz|") or die;
while(<$log>) {
  print $_ if /error/;
}
close $log;

Ceci implique bien entendu que Perl se charge d'exécuter ces programmes à votre demande, c'est une forme altrenative d'invocation de commande (cf. system et l'opérateur backtick).

Note: pour la communication bi-directionnelle avec un programme, voir le module IPC::Open2.

Le fichier '-'

Il existe un fichier spécial nommé - (moins, minus) qui permet de désigner l'entrée standard ou la sortie standard du script, selon la direction utilisée. Il est notable que si on utilise le préfixe pipe, le programme exécuté est une autre instance du script (en d'autre termes, il s'agit d'un fork déguisé).

Appels systèmes directs

Les fonctions d'ouvertures (puis de lecture/écriture) de Perl reposent sur leur équivalent libc fopen, fread, .... On profite ainsi de mécanismes haut niveau: pas de gestion de buffers, d'écritures partielles, etc. Parfois cela peut être un obstacle, surtout si l'on cherche à communiquer avec des fichiers spéciaux (périphériques) ou en utilisant des protocoles binaires.

Dans ce cas on peut utiliser les appels systèmes POSIX standard:

use Fcntl; # Imports O_xxx constants

sysopen(my $port, "/dev/ttyS0", O_RDWR) or die;
while(sysread($port, $buf, 32))  {
  my $to_say  = "You said: $buf\n";
  my $to_send = length($to_say);
  my $sent    = syswrite($port, $to_say, $to_send);
  print "transmit: $sent/$to_send\n";
} 
close $port;

Mode binaire

Sur certains systèmes aux concepts curieux, le mode par défaut d'accès à un fichier n'est pas transparent (un octet pour un octet), mais interprété. Typiquement, un "\n" sera réellement écrit "\r\n" sous MS Windows. On doit alors utiliser explicitement un mode "binaire" pour ces systèmes (la fonction sera sans effet sur les systèmes conventionnels):

binmode($file);

3. Lecture

Par blocs

On peut lire une quantité de donnée quelconque depuis un fichier simplement à l'aide de read:

read($file, $buffer, 1500);
read($file, $buffer, 1500, 2000); # Lit à partir du 2000ème octet
seek($file, 5000); # Positionne le curseur de lecture au 5000ème octet

Par enregistrement

Il s'agit de l'utilisation de loin la courante en Perl, elle est même intégrée en tant qu'opérateur: <>. Le séparateur d'enregistrement peut être modifiée à tout moment à l'aide de la variable spéciale $/ (qui désigne le retour chariot par défaut):

$/ = "\n\n"; # Découpe en "paragraphes"
while (<$file>) {
  print "<p> $_ </p>\n";
}

L'opérateur renvoie le dernier enregistrement lu, ou undef si la fin du fichier est rencontré. Il est ainsi traditionnellement appelé dans le test d'une boucle while, l'enregistrement courant étant alors implicitement dans la variable $_.

Slurp mode

Il est possible de lire un fichier entier en un seul appel en utilisant la chaîne vide comme séparateur, ce qu'on appelle le "slurp mode":

$/ = "";
my $whole = <$file>; # Lit tout le fichier dans $whole

Note: une technique alternative, empruntée au shell serait my $whole = `cat $filename`;.

Enfin dans un contexte de liste, l'opérateur <> permet de lire l'intégralité d'un fichier ligne par ligne en stockang les lignes dans une liste:

open (my $file, "<text");
my @whole = <$file>;
close $file;

4. Ecriture

Formatée

Il existe de nombreuses fonctions de formattage, dont les fameux print, printf, write et format:

print $file "Une information...\n";

Note: write n'est pas le pendant de read ! (Il n'est pas nécessaire, il suffit de manipuler les chaînes et uiliser seek).

Sortie par défaut

Il est possible de changer momentanément le fichier de sortie par défaut, ce qui permet de rediriger facilement la sortie d'un sous-programme sans avoir à le modifier:

make_report(); # Sortie sur STDOUT
my $previous = select($log);
make_report(); # Sortie dans $log
select($previous);

Bufferisation

Par défaut, les appels haut niveau (par opposition à sysread) sont bufferisés par la bibliothèque C standard, pour économiser les appels systèmes. Ce n'est parfois pas désirable, par exemple lorsque l'on veut fournir une sortie caractère par caractère animée (barre de progression, etc).

On peut facilement outrepasser ce cache, par exemple pour la sortie courante (STDOUT par défaut, mais cf. select):

$| = 1; # Autoflush = 1

Et il existe une syntaxe plus générique fonctionnant sur tous les descripteurs de fichiers, mais elle nécessite le module orienté objet:

use FileHandle;

open(my $tty, ">/dev/tty");
$tty->autoflush(1);
Last modified 10 years ago Last modified on Nov 3, 2010, 12:14:08 AM