Les fichiers et Delphi

Bodman

Dans Delphi il existe diverses méthodes de lecture de fichier. Si vous êtes débutant vous avez dû procéder comme moi pendant longtemps en ajoutant un memo invisible dans votre form, car vous connaissiez par cœur les commandes loadfromfile et savetofile de ce composant puis vous avez été face à un problème, le memo il ne lit pas correctement mes fichiers, le memo il est limité en taille de caractères, le memo il est vraiment nul.

Mais non le memo il n’est pas nul c'est juste qu'il n'a jamais été conçu pour ouvrir et fermer les fichiers, non le memo il ne sert qu'à afficher du texte et s'il faut, servir de bloc-notes avec une limite de caractères comme Notepad dans Windows.

Alors là, vous vous êtes peut-être dit : mais comment on lit un fichier ?

- Titre : Les fichiers et Delphi

- Auteur : Bodman

- Parution : 8 mars 2013

- Mise à jour : 7 décembre 2019

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2013 Bodman. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Comment on lit un fichier ?

- II. La sécurité

- III. La lecture/écriture linéaire

- IV. Lecture/écriture par octet (ou variable)

- V. Lecture/écriture par paquet

- VI. D'autres outils pour la gestion de vos fichiers et quelques trucs en plus

- VI-A. Écrire à la fin d'un fichier lors d'un enregistrement par variable ou par paquet

- VII. Conclusion

I - Comment on lit un fichier ?

Pour lire un fichier, il faut déjà savoir de quel genre de fichier il s'agit : texte, suite d'octets ou groupe de variables. Puis sa taille, environs 2 ko ou plus, voire beaucoup plus et même moins.

Là vous saurez quelle méthode choisir, la lecture linéaire, la lecture par octet (par variable) ou la lecture par paquet.

Voilà une description rapide de ces trois types de lecture.

Imaginons un livre.

On peut le lire de plusieurs manières :

- ligne par ligne ;

- lettre par lettre ;

- chapitre par chapitre.

Eh bien les fichiers c'est pareil :

- ligne par ligne ;

- octet par octet ;

- paquet par paquet.

La vraie différence réside dans la vitesse de la lecture/écriture : bloc par bloc c'est plus rapide que octet par octet.

Il y a aussi l'optimisation, car si vous voulez lire un fichier de configuration, vous préférez peut-être le lire ligne par ligne plutôt que par octet, à moins que vous ne préfériez par variable. À vous de choisir.

Après cette petite explication nous allons passer au plus intéressant, comment on fait ?

II - La sécurité

Oui avant toute chose voici une partie très importante lorsque l'on veut utiliser les fichiers, la sécurité.

Il y a plusieurs systèmes, vous aurez le choix entre laisser le programme s'occuper des exceptions ou le contraire (le plus sage pour éviter les plantages).

Voilà la méthode :

{$I+} active la vérification des erreurs et {$I-} la désactive ; vous pouvez faire appel à ceci n'importe où dans votre code.

À chaque erreur si vous avez fait appel à {$I+} une exception EInOutError est déclenchée.

III - La lecture/écriture linéaire

Cette méthode est la première que j'ai apprise et me semble aussi la plus simple.

Pour lire un fichier, tout d'abord il faut utiliser une variable qui contiendra son nom.

Pour le cas d'une lecture linéaire, on considérera que ce fichier sera de type texte.

Donc pour commencer on le déclarera comme variable :

var
F:textfile;

Puis on lui assignera le fichier :

if opendialog.execute then
assignfile(F,opendialog.filename);

Là on a utilisé un opendialog pour trouver le fichier et être sûr de son existence.

Ensuite on va aller au début du fichier, l'initialiser :

reset(F);

C'est tout.

Ensuite passe la lecture proprement dite. Mais on ajoutera une variable string pour contenir nos lignes lors du transfert, ainsi qu'un élément memo pour contenir notre texte.

while not eof(F) do begin //cette boucle répètera la lecture d'une ligne tant que nous n'aurons pas atteint la fin du document EOF
readln(F,lig);//à chaque fois que l'on utilisera readln on passera à la ligne suivante.
memo.lines.add(lig); // ajoute notre ligne au memo
end;
closefile(F);//cette procédure ferme notre fichier F

Bon voilà c'est tout pour la lecture linéaire, maintenant l'écriture.

Pour l'écriture on reprend le code précédent, mais avec quelques modifications :

var
F:textfile;
lig : string;
i:integer;//qui nous servira plus tard
begin
if savedialog.execute then
assignfile(F,savedialog.filename) //là rien ne change même si l'on utilise un savedialog

C'est là que tout change : là on ne va pas utiliser reset(), car on ne veut pas préparer le fichier à la lecture, mais on a le choix entre deux procédures, rewrite() qui va supprimer le fichier F s'il existe et réécrire par-dessus, mais on peut aussi utiliser append() qui ajoutera tout au fichier F et gardera ainsi les données existantes (attention avec append s'il n'y a pas de fichier de ce nom, il y aura une erreur).

Donc on reprendra notre boucle sauf qu'ici ce sera :

rewrite(F);

ou suivant votre choix :

if fileexists(savedialog.filename) then
append(F)
else
rewrite(F);//,car sinon erreur

Puis :

for I := 1 to memo.lines.count do begin
lig := memo.lines[I];
writeln(F,lig) // ici pas le choix;
end;
closefile(F);

Et voilà, j'ai créé une boucle qui lira chaque ligne du memo et l'ajoutera au fichier.

IV - Lecture/écriture par octet (ou variable)

Là c'est un peu plus compliqué, mais attention pas plus dur, je m'explique. Vous aurez à peu près les mêmes méthodes, mais vous devrez tout de même choisir le type de fichier, car ce ne sera pas un textfile donc vous pourrez choisir un file of char, file of integer ou file of je ne sais pas quoi c'est à vous de décider.

Donc voilà l'exemple :

var
F:file of char; // j'ai choisi char mais à vous de choisir
carac:char;
begin
if opendialog.execute then
assignfile(F,opendialog.filename);
reset(F);
while not eof(F) do begin
read(F,carac);//là, plus de readln mais un read tout court qui envoie dans notre char nommé carac un char qui vient de notre file of char nommé F
memo.lines.text :=memo.lines.text+carac;//on ajoute au memo chaque char
end;
closefile(f);
end;

Pas de problème, je pense, donc passons à l'écriture :

var
F : file of integer;
B: array[1..6] of integer;
I : integer;
begin
for I := 1 to 6 do
B[I] := random(1000);
if savedialog.Execute then
begin
assignfile(F,savedialog.filename);
rewrite(F);
for I := 1 to 6 do
write(F,B[I]);
closefile(F);
end;

Commentaire : Ici j'ai changé de type de fichier (que je suis sournois !). Cette fois-ci ce sont des chiffres aléatoires entre 0 et 1000 qui seront ajoutés au fichier. Vous pourrez regarder le fichier, vous serez surpris, je pense, car les chiffres seront marqués en hexadécimal, ce qui donne un truc assez abstrait dans un bloc-notes, mais ne vous inquiétez pas, dans un éditeur hexadécimal avec une calculette vous pourrez vous assurer que vos chiffres ont été bien interprétés.

Nous avons initialisé un fichier typé integer (file of integer) avec la valeur donnée avec notre dialogue de sauvegarde (savedialog) puis on l'a initialisé pour l'écriture, ensuite avec une boucle qui tournera autant de fois qu'il y a de chiffres à donner, on écrira le chiffre de position I qui se trouve, puis à la fin de la boucle on ferme le fichier.

V - Lecture/écriture par paquet

Voilà la partie la plus difficile de mon tutoriel, car assez libre dans l'ensemble.

Bon on va y aller doucement, car ce serait bête de devoir tout relire.

Alors voilà, comme toute lecture (oui on va commencer par la lecture) on va assigner le fichier à F notre variable file préférée :

var
F:file; // Ah oui c'est bon on est arrivé dans la cour des grands, maintenant ce sont des fichiers purs que l'on utilise et pas des fichiers de quelque chose.
Buf: array[1..1024] of Char;//on verra plus tard
lus : integer; //idem
begin
if opendialog.execute then
assignfile(f,opendialog.filename);//là c'est pas dur
reset(F,1);

Oh zut c'est quoi ce truc après F ? Ce 1 qui chamboule toutes les habitudes, c'est quoi ?

Eh bien c'est la taille d'enregistrement lors d'un transfert. Cette valeur n'est à fournir que pour des fichiers non typés (file tout court, ce qui est le cas en ce moment). Je vous conseille de mettre 1, car c'est une valeur universelle (aucun fichier n'est encore à virgule, pour l'instant lol) en plus si vous ne mettez rien ce sera 128 par défaut et si vous mettez autre chose vous devez être sûr que cette valeur marche sinon plantage général sur votre machine (j'ai testé !!).

repeat
BlockRead(F, Buf, SizeOf(Buf),lut);
memo1.lines.text := memo1.lines.text + buf;
until lus =0;
closefile(F);

Bon ici vous allez apprendre une nouvelle fonction blockread et vous allez comprendre à quoi notre buffer (c'est le nom que l'on donne à une variable qui sert à stoker des données) nommé buf sert.

On a une boucle (comme d'habitude) qui lit un bloc et le met dans buf puis l'ajoute au memo.

Donc blockread il fonctionne comme cela :

blockread(variable de fichier, là où on met ce qu'on lit, on donne nombre de choses qu'on va lire, renvoie ce qui a été réellement lu).

Petite explication :

À chaque tour de la boucle, on lit 1024 octets du fichier puis on les ajoute au memo. S'il y en a moins de 1024 à lire on rendra le reste, là c'est la fonction blockread qui s'en chargera. Par contre, si ce qui a été réellement lu est 0, alors on arrête la boucle de lecture et on ferme.

Maintenant passons à l'enregistrement.

Pour l'exemple, nous allons garder celui de la lecture sauf que cette fois-ci nous n'utiliserons pas un memo mais nous créerons un nouveau fichier.

Donc l'exemple précédent était :

var
F:file;
lut : integer;
Buf: array[1..024] of Char;
begin
if opendialog.execute then
assignfile(F,opendialog.filename);
reset(F,1);
repeat
BlockRead(F, Buf, SizeOf(Buf),lut);
memo.lines.text := memo.lines.text + buf;
until lus =0;

closefile(F);

Et notre nouvelle version est :

var
F, F2: file;
lus,ecrit:integer;
Buf: array[1..1024] of Char;
begin
if OpenDialog.Execute then
if SaveDialog.Execute then
begin

AssignFile(F, OpenDialog.FileName);
Reset(F, 1);
AssignFile(F2, SaveDialog.FileName);
Rewrite(F2, 1);

repeat
BlockRead(F, Buf, SizeOf(Buf), lus);
BlockWrite(F2, Buf, lus, ecrit);
until (lut = 0) or (ecrit <> lus )
CloseFile(F);
CloseFile(F2);
end;
end;

Là on utilise un opendialog et un savedialog pour être sûr de l'existence des deux fichiers. Puis on les ouvre et on les initialise (vous avez remarqué que reset avait une option facultative et que rewrite a la même).

Ensuite une boucle apparaît, elle décrit la lecture et l'écriture. Là nous allons nous arrêter pour faire quelque chose que j'aime bien, car c'est pour moi un très bon moyen de comprendre, c'est la traduction du code :

repeat

On répète :

BlockRead(F, Buf, SizeOf(Buf), lut);

Bloc de 1024 octets ( taille de buf ) à envoyer sur buf depuis F, assigner à lus le nombre d'octets réellement lus :

BlockWrite(F2, Buf, lut, ecrit);

Bloc de lus octets (dont le nombre d'octets est égal au nombre lu lors de la lecture juste au-dessus) à envoyer sur F depuis buf, assigner à écrit le nombre d'octets réellement écrits

until (lus = 0) or (ecrit <> lut )

Et on répète cela tant qu'il n'y a pas lut = 0 donc tant qu'on lit quelque chose ou alors tant qu'il n'y a pas écrit <> lut donc tant qu'écrit et lut sont égaux (tant qu'il n'y a pas de problèmes, car ce serait un problème si l'on écrivait plus ou moins que ce que l'on a lu.)

Cette seconde condition est plus là comme sécurité qu'autre chose.

Et bien sûr pour finir, on ferme les fichiers.

Petit truc au sujet de lus et écrit : cette dernière partie de blockread et blockwrite est facultative donc si vous n'en avez pas besoin vous pouvez l'omettre.

VI - D'autres outils pour la gestion de vos fichiers et quelques trucs en plus

VI-A - Écrire à la fin d'un fichier lors d'un enregistrement par variable ou par paquet

Comme vous l'avez vu append ne marche que pour les fichiers de type textfile donc voici une méthode pour la réaliser : déjà pour écrire on peut aussi utiliser reset() cela ne supprimera pas le fichier, mais mettra le curseur au début du fichier. Conséquence si l'on écrit tout sera mis par-dessus, par contre si on met le curseur à la fin du fichier tout sera ajouté.

reset(F);
seek(F,filesize(F));

fileexists(fiche); fonction boolean qui renvoie l'existence du fichier, dont le chemin d'accès est fiche.

deletefile(fiche); efface le fichier fiche.

Attention ces procédures réclament l'ouverture du fichier avec les procédures que vous venez d'apprendre ( reset() …).

Erase(F); efface le fichier F ouvert.

FilePos(F); donne la position du curseur dans le fichier F.

FileSize(F); taille du fichier F qui n'est pas de type texte.

Ioresult boolean qui s'il est false indique une erreur.

rename(F, nouveau); renomme le fichier F en nouveau.

truncate(F); supprime tout ce qui se trouve après le pointeur du fichier F.

seek(F,t); ramène à la position t (t est un longint)du fichier F.

VII - Conclusion

C'est fini il ne vous reste plus qu'à télécharger les exemples de ce tutoriel si vous en avez besoin et si vous avez des problèmes des critiques ou autre chose voici mon mail, bodman@createur.org.