%%  Format et mise en page 

\def\presentation{
\voffset -.50in  \hoffset -.19in
\oddsidemargin 0in \evensidemargin 0in
\marginparwidth .75in \marginparsep 7pt \topmargin 0in
\headheight 12pt \headsep .25in
\footheight 18pt \footskip .35in
\textheight 9.5in \textwidth 6.5in
\columnsep 10pt \columnseprule 0pt }

\def\program#1{{\it{#1}}}
\def\programwithlink#1#2{{\it{{#1}/{#2}}}}
\def\href#1#2{#1\footnote{#2}}
\def\option#1{-#1}
\def\longoption#1{- -#1}
\def\filename#1{{\bf{#1}}}
\def\filepath#1{{\bf{#1}}}

\def\bookref#1{{\it{#1}}}
\def\errormessage#1{{\it{#1}}}
\def\directoryname#1{{\it{#1}}}
\def\command#1{{\it{#1}}}
\def\codeline#1{{\it{#1}}}
\def\function#1{{\it{#1}}}
\def\alert#1{{\bf{#1}}}
\def\commandarray#1{\begin{center}{\it{#1}}\end{center}}
\def\paperwithlink#1#2{{\it{#1}}}
\def\equline#1{\begin{math}#1\end{math}}

\documentclass[a4paper,11pt,twoside,openright]{book}
\voffset -.50in  \hoffset -.19in
\oddsidemargin 0in \evensidemargin 0in
\marginparwidth .75in \marginparsep 7pt \topmargin 0in
\headheight 12pt \headsep .25in
\textheight 9.5in \textwidth 6.5in
\columnsep 10pt \columnseprule 0pt 

\usepackage[francais]{babel}
\usepackage[french]{minitoc}
\usepackage{longtable}

\begin{document}


\setcounter{chapter}{3}

\chapter{Les fichiers}

\section{Tests de bases sur les fichiers}


Si nous regardons notre connaissance actuel de PERL, nous avons d\'ej\`a acquis une certaine comp\'etence en programmation mais nous restons tr\`es limit\'es d\`es qu'il s'agit d'\'echanges avec l'ext\'erieur: nous savons r\'ecup\'erer des cha\^{\i}nes de caract\`eres entr\'ees au clavier et en afficher \`a l'\'ecran. Ce chapitre va essayer de nous apprendre \`a effectuer des entr\'ees/sorties dans les fichiers et \`a manipuler ces derniers.

Avant de lire ou d'\'ecrire dans un fichier, il est en g\'en\'eral int\'eressant de savoir si ce fichier existe, s'il est accessible en \'ecriture... Pour cela, PERL offre plusieurs tests:\\

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&{\textdollar}fichier = "toto";\\
4.&if (-e {\textdollar}fichier)\\
5.&\hspace{0.25cm}\{\\
6.&\hspace{0.5cm}print ("le fichier ".{\textdollar}fichier." existe{\textbackslash}n");\\
7.&\hspace{0.5cm}if (-d {\textdollar}fichier)\\
8.&\hspace{0.75cm}\{\\
9.&\hspace{1cm}print ({\textdollar}fichier." est un repertoire{\textbackslash}n");\\	
10.&\hspace{0.75cm}\}\\
11.&\hspace{0.5cm}else\\
12.&\hspace{0.75cm}\{\\
13.&\hspace{1cm}print ({\textdollar}fichier." est un fichier{\textbackslash}n");	\\
14.&\hspace{0.75cm}\}\\
15.&\hspace{0.5cm}if (-r {\textdollar}fichier)\\
16.&\hspace{0.75cm}\{\\
17.&\hspace{1cm}print ({\textdollar}fichier." est accessible en lecture{\textbackslash}n");\\	
18.&\hspace{0.75cm}\}\\
19.&\hspace{0.5cm}if (-w {\textdollar}fichier)\\
20.&\hspace{0.75cm}\{\\
21.&\hspace{1cm}print ({\textdollar}fichier." est accessible en ecriture{\textbackslash}n");\\	
22.&\hspace{0.75cm}\}\\
23.&\hspace{0.5cm}if ((-f {\textdollar}fichier) \&\& (-x {\textdollar}fichier))\\
24.&\hspace{0.75cm}\{\\
25.&\hspace{1cm}print ({\textdollar}fichier." est fichier executable{\textbackslash}n");\\	
26.&\hspace{0.75cm}\}\\
27.&\hspace{0.25cm}\}\\
28.&else\\
29.&\hspace{0.25cm}\{\\
30.&\hspace{0.5cm}print ("le fichier ".{\textdollar}fichier." n'existe pas{\textbackslash}n");\\
31.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}


Ce petit programme montre le panel des tests possibles sur un fichier dont le nom est ici donn\'e par la variable \codeline{{\textdollar}fichier}. La ligne 4 teste si le fichier existe: \codeline{-e nom\_du\_fichier} retourne la valeur vrai si le fichier existe et faux dans le cas contraire. Le reste du programme montre d'autres tests possibles :
\begin{itemize}
\item \codeline{-d} teste si le fichier est un r\'epertoire (ligne 7).
\item \codeline{-f} teste si le fichier est bien un fichier et non un r\'epertoire (ligne 23).
\item \codeline{-r} teste si le fichier est accessible en lecture (ligne 15).
\item \codeline{-w} teste si le fichier est accessible en \'ecriture (ligne 19).
\item \codeline{-x} teste si le fichier est ex\'ecutable (ligne 23).
\end{itemize}

Vous remarquerez que le \function{if} de la ligne 23 contient deux conditions. C'est l'occasion de faire un petit retour sur l'\'ecriture des conditions. Le symbole \codeline{\&\&} d\'esigne le et logique entre deux conditions. Le ou logique s'\'ecrit \codeline{\textbar\textbar}. Les r\`egles de distributivit\'e sont les m\^eme qu'en math\'ematiques. La seule chose \`a laquelle vous devez pr\^eter attention lorsque vous combinez des conditions, est que ce sont de tests fain\'eants: ils sont a priori effectu\'es de la gauche vers la droite et d\`es qu'une des conditions permet \`a PERL de savoir si le test n'est pas r\'ealis\'e, il s'arr\^ete alors et n'effectue pas le reste des tests. Dans l'exemple de la ligne 23, si notre programme est un r\'epertoire, PERL s'arr\^etera d'\'evaluer les conditions suivant \codeline{(-f {\textdollar}fichier)}. Si vous vous posez la question de savoir pourquoi nous effectuons un test si compliqu\'e pour savoir si le fichier est ex\'ecutable, c'est qu'un r\'epertoire est toujours consid\'er\'e comme ex\'ecutable, il est donc pr\'ef\'erable de les exclure de ce type de test.

Si vous ex\'ecutez ce programme, la question qui vient naturellement \`a l'esprit est de savoir o\`u PERL va chercher le fichier. Si le nom de fichier ne contient pas le chemin absolu (c'est \`a dire depuis la racine \directoryname{/} ou depuis le nom d'un lecteur sous Windows comme \directoryname{C:}), le programme cherche alors uniquement \`a partir du r\'epertoire courant, c'est \`a dire celui o\`u vous avez ex\'ecut\'e le programme. Pour se placer dans un r\'epertoire donn\'e dans un programme, il suffit d'utiliser la commande \function{chdir} qui prend comme argument le nom du r\'epertoire. Une remarque si vous utlisez PERL depuis Windows, le s\'eparateur entre nom de r\'epertoire est le slash \directoryname{/} et non le backslash \directoryname{\textbackslash} afin de rendre vos scripts compatibles avec le reste du monde.



\section{Lecture et \'ecriture de fichiers}


Maintenant que nous sommes en mesure de v\'erifier qu'un fichier est bien pr\'esent et accessible en lecture, nous pouvons le lire en toute tranquilit\'e. Voici un exemple de lecture de fichier:

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&open (INFILE, "test.txt");\\
4.&while ({\textdollar}ligne = {\textless}INFILE{\textgreater})\\
5.&\hspace{0.25cm}\{\\
6.&\hspace{0.5cm}print ({\textdollar}ligne);\\
7.&\hspace{0.25cm}\}\\
8.&close (INFILE);\\
\hline
\end{longtable}
\end{center}


La ligne 3 ouvre en lecture le fichier \filename{test.txt}. Ceci se fait via la commande \function{open} qui prend deux arguments. Le premier est le descripteur de fichier, il servira par la suite \`a identifier le fichier que nous allons ouvrir. Le second argument est le nom du fichier \`a ouvrir. Il est tout \`a fait possible d'utiliser une variable ou une combinaison de cha\^{\i}nes et de variables.

Le descripteur de fichier est en g\'en\'eral indiqu\'e en majuscule (bien que cela n'ait aucun caract\`ere obligatoire). N'importe quel nom peut convenir (hormis s'il s'agit d'un nom r\'eserv\'e PERL), mais \codeline{INFILE} est tr\`es usit\'e. Le descripteur de fichier est \`a rapprocher des descripteurs standards comme STDIN (entr\'ee standard), STDOUT (sortie standard) ou encore STDERR (sortie standard d'erreurs).  

L'op\'erateur diamant \codeline{{\textless}{\textgreater}} permet d'obtenir successivement toutes les lignes (c'est \`a dire les cha\^{\i}nes de caract\`eres se terminant par un retour par un saut de ligne). Tel le Monsieur Jourdain de l'informatique, vous faisiez de l'op\'erateur diamant sans m\^eme le savoir avec \codeline{{\textless}STDIN{\textgreater}}. Ainsi la ligne 4 permet de r\'ecup\'erer ligne par ligne le contenu du fichier dans la variable \codeline{{\textdollar}ligne}. Notez que certaines versions de PERL peuvent vous demander une synatxe un peu plus s\'ecuris\'ee de la forme \codeline{while (defined({\textdollar}ligne = {\textless}INFILE{\textgreater}))}, la fonction \function{defined} permettant de v\'erifier qu'une variable (ici la valeur de retour de l'affectation) est bien d\'efinie. Cette fonction peut aussi s'av\'erer utile dans d'autres occasions comme la recherche dans une table de hash pour savoir si une valeur est associ\'ee \`a une cl\'e, ou si un \'el\'ement d'un tableau est d\'efini.

Une fois que nous en avons termin\'e avec le fichier, il est n\'ecessaire de le fermer, ce qui est fait ligne 8 gr\^ace \`a l'instruction \function{close}.

Notre programme souffre d'un l\'eger d\'efaut. S'il ne parvient pas \`a ouvrir le fichier, rien ne se produit, m\^eme pas un message d'erreur. Rem\'edions \`a cela en rempla\c{c}ant la ligne 3 par:\\

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&unless (open (INFILE, "test.txt"))\\
2.&\hspace{0.25cm}\{\\
3.&\hspace{0.5cm}die ("impossible de lire le fichier test.txt{\textbackslash}n");\\
4.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}


L'instuction \function{unless} applique le bloc d'instruction qui la suit si la condition indiqu\'ee n'est pas v\'erifi\'ee. Elle est donc \'equivalente \`a un \codeline{if (!(condition))}. L'instruction \function{open} retourne la valeur faux si une erreur est survenue lors de l'ouverture du fichier. L'instruction \function{die} permet d'arr\^eter net l'ex\'ecution du script en affichant un message (optionel) pass\'e en param\`etre. Afin de comprendre pourquoi un programme ne marche pas (et cela vous arrivera n\'ecessairement), il est fortement conseill\'e d'au moins faire ce test basique lors de l'ouverture (il est possible de raffiner ce type de test mais nous n'aborderons pas ce point ici).

L'\'ecriture dans un fichier est tout aussi simple. Voici un exemple qui va \'ecrire les premiers factoriels dans un fichier

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&unless (open (OUTFILE, "{\textgreater}factoriel"))\\
4.&\hspace{0.25cm}\{\\
5.&\hspace{0.5cm}die ("impossible de creer le fichier factoriel{\textbackslash}n");\\
6.&\hspace{0.25cm}\}\\
7.&{\textdollar}fact = 1;\\
8.&for ({\textdollar}iter = 1; {\textdollar}iter {\textless}= 10; {\textdollar}iter++)\\
9.&\hspace{0.25cm}\{\\
10.&\hspace{0.5cm}{\textdollar}fact *= {\textdollar}iter;\\
11.&\hspace{0.5cm}print OUTFILE ({\textdollar}fact."{\textbackslash}n");\\
12.&\hspace{0.25cm}\}\\
13.&close (OUTFILE);\\
\hline
\end{longtable}
\end{center}


Par rapport \`a l'exemple pr\'ec\'edent, la seule diff\'erence, hormis le choix d'un autre nom tout aussi arbitraire que \codeline{INFILE} pour le descripteur de fichier, est la pr\'esence du signe \codeline{{\textgreater}} devant le nom du fichier. C'est ce symbole qui indique que le fichier doit \^etre ouvert en \'ecriture. Si un fichier portant le m\^eme nom est d\'ej\`a pr\'esent, il sera alors \'ecras\'e (si vous en avez les droits). Si vous souhaitez \'ecrire \`a la suite d'un fichier d\'ej\`a existant, il suffit de remplacer le symbole \codeline{{\textgreater}} par \codeline{{\textgreater}{\textgreater}}. L'autre diff\'erence, si vous mettez de c\^ot\'e le calcul du factoriel) est la ligne 11. Pour \'ecrire dans un fichier ouvert, il suffit d'intercaler entre un \function{print} et la cha\^{\i}ne de caract\`eres que vous souhaitez enregistrer, le descripteur de fichier. En r\'ealit\'e, lorsque vous n'indiquez aucun descripteur pour une fontion \codeline{print}, PERL suppose implicitement qu'il s'agit du descripteur de la sortie standard (\codeline{STDOUT}), c'est \`a dire la sortie \'ecran. Ainsi un \codeline{print ("toto");} est \'equivalent \`a \codeline{print STDOUT ("toto");}.



\section{Manipuler les fichiers}


Nous avons maintes fois insist\'e sur l'une des forces des scripts tel que PERL face aux applications graphiques lorsqu'il s'agit de manipuler des nombreux fichiers ou d'effectuer dessus des t\^aches r\'ep\'etitives. Il est d\'esormais temps d'en faire la preuve.

Tout d'abord, regardons comment obtenir la liste des fichiers contenu dans un r\'epertoire. Voici comment se faire une petite commande \command{ls} (ou \command{dir} pour les amateurs de DOS):\\

\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&foreach {\textdollar}fichier ({\textless}*{\textgreater})\\
4.&\hspace{0.25cm}\{\\
5.&\hspace{0.5cm}print ({\textdollar}fichier."{\textbackslash}n");\\
6.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}


Si vous placez le symbole \codeline{*} dans l'op\'erateur diamant, vous obtenez un it\'erateur sur les fichiers du r\'epertoire courant. Notez que nous avons utilis\'e \function{foreach} pour cet it\'erateur, mais nous aurions aussi pu utiliser une boucle \function{while} comme pour la lecture d'un fichier. L'\'etoile (ou op\'erateur joker/wildcard) est le m\^eme que celui que vous utilisez lorsque vous faites un \command{ls *.pl} pour filtrer uniquement les fichiers ayant l'extension pl. Il est d'ailleurs possible de filtrer les fichiers avec une extension donn\'ee en utilisant la m\^eme technique. L'\'equivalent d'un \command{ls *.pl} s'\'ecrit:\\
 
\begin{center}
\begin{longtable}{|rl|}
\hline
1.&\#!/usr/bin/perl\\
2.&\\
3.&foreach {\textdollar}fichier ({\textless}*.pl{\textgreater})\\
4.&\hspace{0.25cm}\{\\
5.&\hspace{0.5cm}print ({\textdollar}fichier."{\textbackslash}n");\\
6.&\hspace{0.25cm}\}\\
\hline
\end{longtable}
\end{center}


Quatre autres m\'ethodes utiles lors de la manipulation des fichiers/r\'epertoires sont:
\begin{itemize}
\item \function{rename} permet de renommer un fichier. Elle s'utilise comme suit \codeline{rename(ancien\_nom, nouveau\_nom)}.
\item \function{unlink} permet d'effacer le fichier dont le nom est pass\'e comme argument. 
\item \function{mkdir} permet de cr\'eer un r\'epertoire dont le nom est pass\'e comme argument.
\item \function{rmdir} permet d'effacer  un r\'epertoire dont le nom est pass\'e comme argument.
\end{itemize}

Toutes ces fonctions, comme \function{chdir} ou \function{open}, retournent la valeur faux si jamais une erreur est survenue au cours de l'op\'eration. Notez que vous pouvez directement utiliser les outils de votre syst\`eme pour effectuer ce genre de manipulation, mais vous perdez le c\^ot\'e portable d'un syst\`eme \`a un autre de votre code. Ceci sera d\'ecrit au chapitre 8.



\section{Exercices}


\begin{itemize}
\item[1] Ecrivez un programme qui cr\'ee cent fichiers \filename{testxx} (avec \filename{xx} allant de 0 \`a 99), chaque fichier devant contenir son propre nom. Ensuite cr\'eez un deuxi\`eme programme qui va renommer chacun de ces fichiers de \filename{testxx} en \filename{totoyy} avec \filename{yy = xx+100}.
\item[2] Ecrivez un programme qui cr\'ee un fichier \filename{donnees1.dat} dont chaque ligne est une valeur de l'\'echantillonage des nombres entre 0 et 1 avec un pas de 0.01. Faire un deuxi\`eme programme qui va lire le fichier pr\'ec\'edent. Pour chaque ligne lue, le programme devra extraire la valeur num\'erique \codeline{x} contenue, calculer \codeline{cos(\equline{2\pi}x)} et stocker dans un fichier \filename{donnees2.dat} une ligne compos\'ee de \codeline{x} et de la valeur de la fonction pr\'ec\'edente \'evalu\'ee en ce point (les deux nombres seront s\'epar\'es par un espace). 
\end{itemize}





\end{document}


