Chapitre 10 - Manipulation des fichiers▲
Déclaration▲
Pour utiliser un ou des fichiers tout au long d'un programme ou dans une procédure, il faudra l'identifier par une variable dont le type est fonction de l'utilisation que l'on veut faire du fichier.
Il existe trois types de fichiers :
Les fichiers textes (Text)▲
Les fichiers textes (Text) sont écrits au format texte (chaînes de caractères, nombres) dans lesquels ont peut écrire et lire ligne par ligne ou à la file avec les procédures Write(Ln) et Read(Ln).
Chaque fin de ligne du fichier se termine par les caractères 10 et 13 de la table ASCII (qui signifient respectivement Retour ligne (Line Feed, LF) = #10 et Retour chariot (carriage return, CR) = #13).
Ces deux derniers caractères sont transparents au programmeur.
On pourra donc écrire ou lire indifféremment des chaînes ou des nombres, cela dépend du type que l'on affecte à la variable passée en paramètre aux procédures d'entrée/sortie (voir plus bas).
S'il y a lieu de faire une conversion nombre/chaîne, le compilateur le fait tout seul. Par contre, si le type de la variable ne correspond pas avec la donnée lue dans le fichier et qu'aucune conversion n'est possible alors cela produit une erreur.
Syntaxe :
Var
f : Text;
Les fichiers typés (File of ...)▲
Les fichiers typés (File of) sont des fichiers dans lesquels les données sont écrites telles qu'elles se présentent en mémoire.
On dit qu'elles sont écrites dans leur représentation binaire. La taille du fichier résultera directement et exactement de la taille en mémoire qu'occupe telle ou telle variable.
Cela accroît la vitesse d'accès aux données du fichier.
Mais le plus grand avantage c'est que l'on obtient ainsi des fichiers parfaitement formatés, c'est-à -dire qu'on peut y lire et écrire directement des variables de type structuré qui contiennent plusieurs champs de données sans avoir à se soucier des divers champs qu'elles contiennent.
Syntaxe :
Var
f : File
of
{type}
;
Exemple :
Var
f : File
of
Integer
;
Les fichiers simples (File)▲
Les fichiers... tout court ! (File) sont des fichiers dont on ne connaît pas le contenu.
Ils peuvent servir à faire une simple copie d'un fichier dans un autre, pour stocker des données de différents types (en se donnant les moyens de retrouver ce que sont ces types lors de la lecture), pour sauver tout le contenu d'une zone mémoire pour analyse ultérieure, etc.
Syntaxe :
Var
f : File
;
Lecture et écriture▲
Avant de travailler sur un fichier, il faut le déclarer en lui affectant une variable qui servira à désigner le fichier tout au long du programme ou de la procédure dans laquelle il est utilisé.
Assign s'applique à tous les types de fichiers.
Syntaxe :
Assign (variable_d_appel, nom_du_fichier);
Ensuite, il faut ouvrir le fichier.
Syntaxe :
Reset (variable_d_appel);
On ne peut pas écrire dans un fichier Text ouvert avec Reset !
Il est possible, pour le type File uniquement, de spécifier la taille de chaque bloc de donnée lu ou écrit sur le fichier, en rajoutant en argument à Reset une variable (ou un nombre directement) de type Word (entier) spécifiant cette taille en octet.
Cela nécessite de connaître la taille mémoire de chaque type de variable (voir chapitre 4 sur les différents types de variables) - la fonction SizeOf sert à cela.
Par exemple, cette taille vaudra 6 si on veut lire des nombres réels (Real) ou bien 256 pour des chaînes de caractères (String).
Le fait que la variable taille soit de type Word implique que sa valeur doit être comprise entre 0 et 65535.
Par défaut, taille = 128 octets.
Syntaxe :
Reset (variable_d_appel, taille);
Pour créer un fichier qui n'existe pas ou bien pour en effacer son contenu, on emploie Rewrite qui permet d'effectuer des lectures (File of et File) et écritures (Text, File of et File).
On ne peut pas lire dans un fichier Text ouvert avec Rewrite !
Syntaxe :
Rewrite (variable_d_appel);
L'utilisation de Rewrite avec un fichier existant ouvre ce fichier, mais le vide !
Tout comme Reset, Rewrite permet de spécifier une taille aux échanges de données sur un File seulement (aussi bien en écriture qu'en lecture).
Avec Rewrite, dans le cas où le fichier n'existe pas encore alors qu'avec Reset c'est dans le cas où il existe déjà .
Syntaxe :
Rewrite (variable_d_appel, taille);
Syntaxe | Lecture | Ecriture |
---|---|---|
Reset(f) | Text File of File |
File of File |
Rewrite(f) | File of File |
Text File of File |
Reset(f,taille) | File | File |
Rewrite(f,taille) | File | File |
Pour lire le contenu d'une ligne d'un fichier Text ouvert, on utilise la même instruction qui permet de lire la valeur d'une variable au clavier à savoir ReadLn.
(2)
Sera alors lue, la ou les variable(s) correspondant au contenu de la ligne courante (celle pointée par le pointeur).
Si la ou les variable(s) n'étai(en)t pas de taille suffisante pour contenir toutes les données de la ligne, alors l'excédent serait perdu.
C'est même un peu plus compliqué que ça : si on lit une variable de type String, la lecture se poursuivra jusqu'à la fin de la ligne en cours, ou jusqu'à la fin du fichier si elle survient avant, et cela quelle que soit la longueur maximale possible pour la chaine.
Il faut donc faire très attention.
Syntaxes :
ReadLn (variable_d_appel, variable);
ReadLn (variable_d_appel, var1, var2, ... varN); { Attention, pas de Strings (sauf la dernière) }
Pour écrire dans un fichier Text, il suffit d'employer la procédure WriteLn.
(3)
Syntaxes :
WriteLn (variable d_appel, variable);
WriteLn (variable_d_appel, var1, var2, ... varN); { Attention : voir la remarque ci-après }
Il faut éviter cette seconde syntaxe, à moins de préciser, pour chaque variable à écrire, la largeur minimum à utiliser, en prenant soin que pour tous les cas possibles cette largeur minimum garantisse qu'il y aura au moins un espace pour séparer les données de chaque variable, sinon on ne pourra pas relire correctement les données.
Pour lire et écrire sur un File, il faut utiliser les procédures BlockRead et BlockWrite.
Syntaxes :
BlockRead (f, variable, nbr);
BlockRead (f, variable, nbr, result);
BlockWrite (f, variable, nbr);
BlockWrite (f, variable, nbr, result);
BlockRead lit sur le fichier f de type File une variable qui contiendra un nombre de blocs mémoire (dont la taille est définie par Reset ou Rewrite) égale à nbr.
La variable (facultative mais dont l'utilisation systématique est fortement conseillée) result prend pour valeur le nombre de blocs effectivement lus (car il peut y en avoir moins que prévu initialement).
BlockWrite écrit sur le fichier f de type File une variable sur un nombre de blocs mémoire égal à nbr.
La variable (facultative mais dont l'utilisation systématique est fortement conseillée) result prend pour valeur le nombre de blocs effectivement écrits.
Dans le cas ou cette variable result est ommise et qu'il est impossible d'écrire autant de blocs que voulu, il est généré une erreur d'exécution !
Les variables nbr et result doivent être de type Word.
Syntaxe | Types de fichiers associés |
---|---|
WriteLn | Text |
ReadLn | Text |
Write | Text File of |
Read | Text File of |
BlocWrite | File |
BlockRead | File |
Fermeture▲
Il est impératif de fermer les fichiers ouverts avant de terminer le programme, sous peine de voir les données inscrites en son sein perdues.
Syntaxe :
Close (variable_d_appel);
Il est possible de rouvrir un fichier Text en cours de programme même s'il a déjà été refermé, grâce à sa variable d'appel.
La position courante du curseur sera alors positionnée à la fin du fichier.
Cela ne fonctionne qu'en écriture et qu'avec un Text.
Syntaxe :
Append (variable_d_appel);
Exemple :
Program
Exemple12;
Uses
Crt, Dos;
Var
f : Text;
Nom : String
;
Choix : Char
;
Procedure
Lecture;
Begin
Append(f);
Readln(f,nom);
Writeln(nom);
End
;
BEGIN
Clrscr;
Assign(f,'init.txt'
);
Rewrite(f);
Write
('Entrez un nom d''utilisateur : '
);
Readln(Nom);
Nom := 'Dernier utilisateur : '
+ Nom;
Writeln(f,Nom) ;
Close(f);
Write
('Voulez-vous lire le fichier init.txt ? [O/N] '
);
Choix := ReadKey;
if
(UpCase(Choix) = 'O'
) then
Lecture;
END
.
Ce programme Exemple12 illustre les principales commandes qui permettent de travailler sur des fichiers de type texte. Ici, le programme réinitialise à chaque lancement le fichier init.txt et y inscrit une valeur entrée par l'utilisateur (son nom, en l'occurrence). Il permet également à l'utilisateur de lire le contenu du fichier (qui ne contient qu'une seule ligne de texte).
Fonctions supplémentaires▲
Gestion du curseur▲
Il est possible de déplacer à volonté le curseur en spécifiant à la procédure suivante le fichier de type File of ou File ainsi que le numéro de l'élément (le premier à pour numéro :"0", le second : "1", le troisième : "2", etc...) où l'on veut mettre le curseur.
Cela s'appelle l'accès direct à un fichier contrairement à l'accès séquentiel qui consiste à parcourir toutes les informations précédant celle qu'on cherche.
Note : Seek n'est pas utilisable avec des Text.
Syntaxe :
Seek (variable_d_appel, position);
Exemple :
Program
Exemple13 ;
Uses
Crt, Dos;
Var
f : Text;
s : String
;
BEGIN
Assign(f,'c:\autoexec.bat'
);
Reset(f);
Writeln('Affichage du contenu du fichier AUTOEXEC.BAT : '
);
while
not
Eof(f) do
begin
ReadLn(f,s);
WriteLn(s);
end
;
END
.
Ce programme Exemple13 permet de lire un fichier texte et d'en afficher le contenu à l'écran.
La fonction Eof permet de vérifier si le pointeur arrive en fin de fichier (elle aura alors la valeur true).
Il est possible de connaître la taille d'un fichier en octets, sauf lorsque celui-ci est déclaré en Text.
Syntaxe :
FileSize (variable_d_appel);
Il est possible de connaître la position du pointeur, en octets, dans le fichier lorsque celui-ci est déclaré en File of Byte.
La fonction suivante renvoie une valeur de type LongInt.
Syntaxe :
FilePos (variable_d_appel);
Exemple :
Program
Exemple14;
Var
f : File
of
Byte
;
Taille : LongInt
;
BEGIN
Assign(f,'c:\autoexec.bat'
);
Reset(f);
Taille := FileSize(f);
Writeln('Taille du fichier en octets :'
,Taille);
Writeln('Déplacement du curseur...'
);
Seek(f,Taille div
2
);
Writeln('Le pointeur se trouve à l''octet : '
,FilePos(f));
Close(f);
END
.
Le programme Exemple14 déclare le fichier autoexec.bat comme File of Byte et non plus comme Text, puisqu'on ne désire plus écrire ou lire du texte dedans mais seulement connaître sa taille et accessoirement faire mumuse avec le pointeur.
Fin de ligne, fin de fichier▲
Il est possible de savoir si, lors de la lecture d'un fichier, on se trouve ou non en fin de ligne ou de fichier, grâce aux fonctions suivantes qui renvoient une valeur de type Boolean.
Syntaxe :
Var
f : Text;
Eof(f); { Pointeur en fin de fichier }
SeekEoLn(f); { Pointeur en fin de ligne }
Autre syntaxe :
Var
f : File
;
EoLn (f); { Pointeur en fin de ligne }
SeekEof (f); { Pointeur en fin de fichier }
Exemple :
Program
Exemple15 ;
Var
f : Text;
i, j : String
;
BEGIN
Assign(f,'c:\autoexec.bat'
);
Reset(f);
while
not
SeekEof (f) do
begin
if
SeekEoln(f) then
ReadLn(f);
Read
(f,j);
Writeln(j);
end
;
END
.
Effacer un fichier▲
On peut également effacer un fichier préalablement fermé.
Syntaxe:
Erase (f);
Renommer un fichier▲
On peut aussi renommer un fichier (qui ne doit pas être ouvert).
Syntaxe :
Rename (f, NouveauNom);
Tronquer un fichier▲
Il est possible de tronquer un fichier, c'est-à -dire de supprimer tout ce qui se trouve après la position courante du pointeur.
Syntaxe :
Truncate (f);
Gestion des erreurs▲
Dans tous les exemples de manipulation de fichiers donnés jusqu'ici, il n'a pas été tenu compte (pour des raisons de clarté, pour présenter les différentes notions) des éventuelles erreurs qui auraient pu survenir.
En effet, si on laisse les choses en l'état, la moindre erreur de création, d'ouverture, de lecture, d'écriture de fichier provoquera une terminaison immédiate et inconditionnelle du programme.
Or, il y a moyen d'intercepter proprement toutes ces erreurs et d'offrir à l'utilisateur un produit mieux fini.
La première chose à faire est de donner une directive au compilateur, lui demandant non pas de mettre fin au programme en cas d'erreur, mais plutôt d'aller indiquer un code d'erreur dans une variable interne que le programme pourra consulter.
Cette directive est
{$I-}
Après chaque exécution d'une routine de gestion de fichier, le programme pourra aller tester la valeur de cette variable interne, au moyen de la fonction IOResult.
Exemple : une procédure de copie de fichier :
Function
CopyFile (const
SourceFile, DestFile : String
) : Boolean
;
Var
Source, Dest : File
;
Buffer : Array
[0
..1023
] of
Byte
;
Count : Word
;
OldFileMode : Word
;
Begin
CopyFile := True
;
{ Désactivation des erreurs E/S }
{$I-}
{ Assignation des fichiers }
Assign(Source,SourceFile);
Assign(Dest,DestFile);
{ Ouverture en lecture seule de Source et création de Dest }
OldFileMode := FileMode;
FileMode := 0
;
Reset(Source,1
);
if
IOResult = 0
then
begin
Rewrite(Dest,1
);
if
IOResult = 0
then
begin
{ Boucle principale de copie, s'arrête quand la fin du fichier est atteinte }
repeat
{ Remplissage du buffer : 1 Ko prévus, Count octets réels }
BlockRead(Source,Buffer,SizeOf(Buffer),Count);
{ Ecriture du contenu du buffer }
BlockWrite(Dest,Buffer,Count);
until
(Count = 0
) or
(Count <> SizeOf(Buffer));
{ Fermeture du fichier }
Close(Dest);
if
IOResult <> 0
then
{ Erreur de fermeture du nouveau fichier }
CopyFile := False
;
end
else
{ Erreur de création du nouveau fichier }
CopyFile := False
;
{ Fermeture du fichier }
Close(Source);
end
else
{ Erreur d'ouverture du fichier original }
CopyFile := False
;
{ Réactivation des erreurs d'E/S et rétablissement du mode de lecture }
FileMode := OldFileMode;
{$I+}
End
;
Notez que le mode normal de gestion des erreurs est restauré à la fin de la procédure, avec la directive
{$I+}