> Langage
PrograZine issue #10 - http://www.citeweb.net/discase/10/ Edito Sommaire Contribution Contacts


Initiation au Pascal (V)
par discase - discase@mail.dotcom.fr
Débutant

    Dans ce cours, nous allons voir comment accéder à des fichiers par programme, en Pascal.
>Les types de fichiers

    En Pascal (comme en C ou autres), on distingue plusieurs types de fichiers: les fichiers textes et les fichiers binaires.

    En fait, quelque soit son type, un fichier est toujours la même chose sur le disque dur (ou disquette): c'est un amas d'octets ayant un nom, une taille et diverses informations spécifiques au système d'exploitation (date de création, droits d'accès sous Unix, etc ...). Le type de fichier de Pascal représente la façon dont vont être interprétés les données (i.e les octets) contenues dans le fichier.

    Pour lire un fichier (ou écrire dans un fichier), il faut spécifier plusieurs informations: le mode d'ouverture ,le type d'ouverture et le type de l'élement à lire/écrire
    Prenons le fichier suivant:
Fichier: exemple
Taille: 3 octets
+-----+-----+-----+
| '3' | '4' | '1' |
+-----+-----+-----+
l

    Si on ouvre ce fichier en mode lecture, en type texte et qu'on lit un élément de type entier, on aura lu le nombre 341 et on aura atteint la fin du fichier.

    Si on ouvre ce fichier toujours en lecture, en type texte, et que cete fois on lit un élément de type 'caractère', on aura lu le caractère '3' et on sera en deuxième position dans le fichier

    Maintenant, toujours avec ce même fichier, on décide de l'ouvrir en mode binaire avec une lecture d'élément de type entier court (i.e byte en Pascal). On aura lu ... le nombre 51 !!!

    Pourquoi 51 et non 3 ? En fait, dans le fichier, on a 3 octets, de code ASCII respectifs 51,52 et 49. Ainsi, si on lit un seul élément dans une ouverture binaire de type entier court, on lit le code ASCII du premier octet, soit 51.

    Maintenant, supposons qu'on ouvre toujours ce fichier en mode binaire, mais avec lecture d'un élément de type entier (i.e integer en pascal), on aurau lu le nombre ... 13363 !!

    Décomposons, pour mieux comprendre chaque octet du fichier en binaire:
+----------+----------+----------+
| 00110011 | 00110100 | 00110001 |
+----------+----------+----------+
    (1)        (2)         (3)
l

    Quand on veut lire un nombre de type entier, on lit en fait deux octets, car le type integer prend deux octets en mémoire pour Pascal. On pourrait donc s'attendre à ce qu'on lise le nombre 0011001100110100 (c'est à dire (1)(2)), soit 13108. Ce serait le cas sur une machine avec un processeur Motorola (Mac par exemple), mais ça devient faux avec Intel ...

    Ouvrons une petite parenthèse sur le sujet...

    Quand on parle d'octets, ou de mots (i.e deux mots, ou quatre suivant les systèmes), on a l'habitude de séparer cet élément en deux: la partie de poids forte et la partie de poids faible. Prenons le nombre 160001 en décimal. Imaginons que ce nombre représente le nombre de kilomètres à parcourir pour partir en vacances (!!): il est plus important de savoir qu'il y a environ 160000 kilomètres que plus de 001 kilomètres !! On pourra dire que les premiers chiffres sont la partie forte, parce que la partie la plus significative et les derniers sont la partie de poids faible.

    Ainsi, sur un mots (ici un entier, c'est à dire deux octets), l'octet le plus à gauche dans la représentation binaire est appellé octet de poids fort et les 8 derniers bits, sont appellés octet de poids faible.

    Je ne pense pas qu'il existe une norme indiquant dans quel ordre transmettre les octets de poids forts et faibles. Intel a décidé de transmettre d'abord l'octet de poids faible, puis celui de poids faible. Motorola a décidé du contraire, c'est comme ça !!

Refermons la petite parenthèse ...

    Tout ça pour dire que sur les compatibles IBM PC, quand on lira le premier entier de ce fichier, c'est à dire ses deux premiers octets, on aura lu 13363, c'est à dire 0011010000110011 ((2)(1)).

    Si vous n'avez pas compris la petite parenthèse, ce n'est pas bien grave ...
>Le mode texte

    Nous l'avons vu plus haut, le mode texte est le plus simple, car il correspond directement à ce qu'un humain peut "voir" dans un fichier. Ce mode d'ouverture porte bien son nom. En effet sous l'environnement MS-DOS/Win (et autres ...) il existe des fichiers à l'extension ".txt" que Windows nomme "fichier texte". Quand vous démarrez le bloc-note pour voir ce que contient ce fichier, il vous affiche exactement le contenu du fichier. Pour voi la différence, démarrez Wordpad en ouvrant un fichier de type ".rtf", pas de problèmes. Ouvrez ce même fichier avec le Bloc-Notes, il affiche le contenu réel du fichier, c'est à dire une suite de mots clés. Il en est de même avec le HTML et d'autres ...
>Ouverture

    Avant de faire quoi que ce soit sur un fichier, il faut d'abord "l'ouvrir", c'est à dire au point de vue système, savoir à quelle zone mémoire du disque il faut accéder

    En pascal, on doit d'abord déclarer une variable qui représentera ces informations par la suite et "l'assigner" au fichier que l'on veut ouvrir.
VAR
	Fichier: text;
BEGIN
	assign(Fichier,'exemple.txt');

	...

END.
l

    La variable Fichier a pour type text, qui dit que le fichier sera ouvert en mode texte. Ensuite 'assign(Fichier,'exemple.txt')' dit que cette variable représentera le fichier du disque nommé 'exemple.txt'. Cette instruction est nécessaire, mais le fichier n'est toujours pas ouvert !!

    En effet, pour l'ouvrir il faut spécifier si on l'ouvre en lecture ou écriture...

    Pour l'accès en lecture:
	reset(Fichier);
l

    Pour l'accès en écriture:
	rewrite(Fichier);
l
l Attention, avec 'rewrite', si le fichier n'existait pas, il est créé. Avec 'reset', si le fichier n'existe pas, il y a erreur !
l Dans le cas de l'ouverture en lecture, on utilise reset pour la première fois. On peut réutiliser reset pour revenir au début du fichier
>Lecture

    Pour lire des éléments d'un fichier texte, on utilise la fonction 'readln', la même que pour lire des données à partir du clavier, mais avec quelques différence :
VAR
	Fichier: texte;
	Tampon: integer;
BEGIN
	assign(Fichier,'exemple.txt');
	reset(Fichier);

	Read(Fichier,Tampon);

	Writeln(Tampon);

	...
END.
l

    Cet exemple lit le premier élement du fichier 'exemple.txt'. Pour lire un élément on utilise donc 'readln(fichier, variable)' où fichier représente la variable de type text associée au fichier.

    Dans le cas du fichier précedemment cité, le programme afichera '341'
l A chaque fois qu'on lit un élément, le 'pointeur' de fichier, c'est à dire l'endroit où on se trouve dans le fichier avance d'un.

    Si on veut lire tout le contenu du fichier, on doit faire appel à une variable déjà définie par le Pascal qui indique si la fin du fichier a été atteinte: eof
VAR
	Fichier: text;
BEGIN
	assign(Fichier,'exemple.txt');
	reset(Fichier);

	While not eof(Fichier) do
	begin
		... { traitement sur chaque élément du fichier }
	end;

	...
END.
l

    Si la fin du fichier a été atteinte, eof(Fichier) renvoie True, sinon, elle renvoie False
>Ecriture

    Pour écrire dans un fichier texte, vous l'aurez deviné, il faut utiliser 'Writeln' ...
VAR
	Fichier:text;
	Tampon:integer;
BEGIN
	assign(Fichier,'exemple.tmp');
	rewrite(Fichier);

	Tampon:=123;
	Writeln(Fichier,Tampon);

	...
END.
l

    Rien de plus simple ...
l A chaque fois qu'on écrit un élément, le 'pointeur' de fichier est incrémenté.
l Attention: l'utilisation de 'reset' après 'rewrite' a pour effet de réouvrir le fichier en mode lecture en se plaçant au début du fichier.
>Fermeture

    Après avoir utilisé un fichier, vous devez IMPERATIVEMENT le refermez, pour cela, on utilise 'close(Fichier)'
VAR
	Fichier:text;
	Tampon:integer;
BEGIN
	assign(Fichier,'exemple.txt');
	reset(Fichier);

	While not EOF(Fichier) do
	begin
		read(Fichier,Tampon);
		Writeln(Tampon);
	end;

	close(Fichier);
END.
l

    La fermeture d'un fichier annule les reférences au fichier par la variable de type 'text', mais cette variable est encore utilisable pouir ouvrir un autre fichier. De plus si un fichier a été ouvert en lecture par la variable Fichier, après la fermeture, il est possible d'ouvrir un autre fichier avec toujours la variable 'Fichier', mais en écriture.
>Les fichiers binaires (ou fichiers non typés)

    Les fichiers binaires, comme nous l'avons vu plus haut lisent exactement ce qui se trouve dans le fichier, sans aucune interprétation. En effet, en lecture en mode texte, la lecture de '1', '2', '3', donnait 123, les troi caractères ont étés interpétés et reconnus comme chiffres formant un nombre. En mode binaire, on lira le caractère '1', puis le caractère '2', puis le caractère '3' et éventuellemtn leurs codes ASCCI respectifs si on veut lire des nombres.
>Ouverture

    Avant tout, on doit d'abord déclarer une variable qui représentera le fichier tout au long du traitement de type 'file'

    Les modes d'ouvertures sont les mêmes (lecture et écriture), on appalle alors les mêmes fonctions 'reset' et 'rewrite',à une différence près:
VAR
	fbin: file;
BEGIN
	assign(fbin,'exemple.txt');
	reset(fbin,1);

	...

	close(fbin);
END.
l

    Vous l'aurez noté, on a un autre parmaètre à la fonction 'reset'. En effet, après la variable représentant le fichier, on passe à reset ce qu'on appelle la taille des enregistrements, ceci représente en fait la taile, en octets, de chaque élément du fichier. Dans le cas d'un fichier 'text', chaque élément était en fait un caractère, mais il peut arriver qu'on veuille uniquement transmettre des entiers, comme on le verra plus tard... En général, pour des fichiers binaires, on met 1 pour la taille des enregistrements. Par défaut, elle est initialisée à 128 octets.

    En fait cet argument sert après pour la lecture et l'écriture ...
>Lecture

    Pour lire un élément d'un fichier binaire, on utilise 'blockread'.
VAR
	fbin:file;
	tampon:byte;
BEGIN
	assign(fbin,'exemple.txt');
	reset(fbin,1);

	blockread(fbin,tampon,1);
	
	writeln(tampon);

	close(fbin);
END.
l

    Ce programme lire dans le cas de l'exemple précédent, le nombre 51 et l'affichera, c'est à dire le code ASCII de '3'.

    On passe donc à Blockread la variable représentant le fichier, la variable dans laquelle on veut lire et ... la taille de l'élément à lire. C'est ici qu'intervient le 1 du 'reset'. On a spécifié ici 1 comme taille à blockread. Cela veut dire qu'on veut lire 1 élément d'1 octet et le mettre dans tampon.

    Si on avait ceci
VAR
	fbin:file;
	tampon:integer;
BEGIN
	assign(fbin,'exemple.txt');
	reset(fbin,2);

	blockread(fbin,tampon,1);
	Writeln(tampon);
	close(fbin);
END.
l

    Ici, on ouvre le fichier avec une taille d'enregistrements de 2 octets. A l'appel de blockread, on lit donc 1 élément de deux octets que l'on met dans Tampon (qui fait bien deux octets, car c'est un integer).

    En général, on utilisera ceci:
VAR
	fbin:file;
	tampon:integer;
BEGIN
	assign(fbin,'exemple.txt');
	reset(fbin,1);
	blockread(fbin,tampon,sizeof(tampon);
	writeln(tampon);
	close(fbin);
END.
l

    La fonction 'sizeof' renvoie la taille, en octet, de l'objet passé en paramètre. Dans la plupart des cas, un integer aura une taille de 2 octets, mais peut être qu'il existe des systèmes sur lesquels integer fait 4 octets (je ne sais pas) dans ce cas, notre programme ne serait pas portable ! En utilisant sizeof, on est sûr qu'on aura la bonne taille, puisque la réponse de sizeof est spécifique à chaque système !

    Maintenant, si on veut lire un fichier jusqu'au bout, il y a plusieurs solutions. Soit on fait un test à chaque lecture pour savoir si la position du fichier est égale à la taille du fichier. Ou on utilise un parmaètre facultatif de blockread.

    Première solution:
VAR
	fbin:file;
	tampon:integer;
BEGIN
	assign(fbin,'exemple.txt');
	reset(fbin,1);

	while filepos(fbin)<inf>=filesize(fbin) do
	begin
		blockread(fbin,tampon,sizeof(tampon));
		...
	end;
	close(fbin);
END.
l

    Ici, on utilise la fonction filepos qui renvoie la position courante dans le fichier et filesize qui renvoie la taille du fichier.

    Deuxième solution:
VAR
	fbin:file;
	tampon:integer;
	taille:integer;
BEGIN
	assign(fbin,'exemple.txt');
	reset(fbin,1);

	while (1) do
	begin

		blockread(fbin,tampon,sizeof(tampon),taille);
		if taille<>sizeof(tampon) then
			break;

		...
	end;
	close(fbin);
END.
l

    Le quatrième argument de blockread permet de savoir combien d'octets ont été réellement lus dans le fichier. En cas de fin de fichier, le nombre d'octets effectivement lus est inférieur au nombre prévu.
l La fonction Eof ne peut être utilisée que pour des fichiers ouvert en type 'text', on est donc obligé de recourir a des petites 'bidouilles'.
>Ecriture

    L'écriture dans un fichier binaire se fait apr l'intermédiaire de la fonction 'blockwrite'. Comme pour blockread, elle a trois parmaètres plus un facultatif. Les trois premiers sont la variable représentant le fichier, la variable à écrire et la taille de cette variable. L'argument facultatif est une variable représentant le nombre d'octets effectivement écrits, pouvant servir à la gestion d'erreurs.

    Un petit piège à éviter
VAR
	fbin:file;
BEGIN
	assign(fbin,'exemple.txt');
	rewrite(fbin,1);

	blockwrite(fbin,123,1);

	close(fbin);
END.
l

    Ce programme provoquera une erreur, car il est interdit de passer une expression directement en paramètre. En effet, les fonctions blockread, et blockwrite passent leurs arguments par adresse et non par valeur (cf cours précédent).

    On devra donc écrire:
VAR
	fbin:file;
	tampon:byte;
BEGIN
	assign(fbin,'exemple.txt');
	rewrite(fbin,1);

	tampon:=123;
	blockwrite(fbin,tampon,sizeof(tampon));

	close(fbin);
END.
l
>Les fichiers typés

    Le type 'fichier typé' est un mode spécial d'ouverture. Il découle des fichiers binaires. Ils servent par exemple à la gestion des bases de données. Dans ces fichiers, on ne pourra lire/écrire qu'UN SEUL type de données.
>Ouverture

    On agit de la même façon que pour le reste, sauf qu'ici le type de fichier est 'file of xxx' où xxx représente le type de données:
VAR
	f: file of integer;
BEGIN
	assign(f,'exemple.txt');
	reset(f);

	...

	close(f);
END.
l

    Avec cet exemple on ne pourra lire que des entiers. A la différence des fichiers binaires, où on pouvait lire tout type de données, si on donnait la taille de ces données.

    Notez ici qu'on ne donne pas la taille des enregistrements à reset(ou rewrite), elle est automatiquement égale à la taille des integer, soit deux dans la plupart des cas.
>Lecture/Ecriture

    Pour lire ou écrire dans un fichier typé, on utilise les fonctions Read et Write.
VAR
	f:file of integer;
	tampon:integer;
BEGIN
	assign(f,'exemple.txt');
	reset(f);

	read(f,tampon);
	writeln(tampon);

	close(f);
END.
l
l En fait, les fichiers de type 'text', sont quasiment des fichiers de type 'file of char'
l Comme précedemment, on ne peut pas utiliser la fonction EOF, reservée uniquement aux fichiers 'text'.
l On peut définir un fichier typé de n'importe quel type de base, et aussi des types définis par la suite, comme les enregistrements (ou structures).
>Déplacement dans un fichier

    Il est parfois nécessaire de pouvoir se déplacer à un endroit précis dans un fichier. On utilise pour ça, la fonction 'seek', ayant pour paramètres la variable référençant le fichier et la position à laquelle on veut se déplacer. Cette position est exprimée en nombre d'éléments, et non en nombres d'octets.

    Pour un fichier binaire, ouvert avec un taille d'enregistrements de 1 (i.e la plupart du temps), on pourra faire 'seek(fbin,12)' pour aller au 12ème octet dans le fichier.

    Pour un fichier typé, de type integer, on utilisera: 'seek(f,12)' pour se déplacer au 12ème élément du fichier, c'est à dire au 12*sizeof(integer) ième octet du fichier, soit le 24ième octet.
>Fonctions et fichiers

    On peut accéder à des fichiers dans des fonctions ou procédures. Mais, IL FAUT ABSOLUMENT passer un fichier par ADRESSE si on veut utiliser un fichier ouvert.

    Par exemple, une fonction qui se déplace à la fin du fichier
...
PROCEDURE VaALaFin(VAR fichier: file);
BEGIN
	seek(fichier,filesize(fichier));
END;

BEGIN
	assign(fbin,'exemple.txt');
	reset(fbin);

	VaALaFin(fbin);

	...
	close(fbin);
END.
l

    On doit donc absolument passer le fichier avec le mot-clé VAR
>Petit exercice

    Premier exercice:

    Ecrire un programme qui demande d'entrer les renseignements d'une personne dans la structure TFiche, définie dans les numéros précédents. Si le nom est FIN, on arrête. Chaque élément est sauvé dans un fichier

    Deuxième exercice:

    A partie de ce fichier d'enregistrements, créer une procédure qui recherche les renseignements d'une personne dont on a que le nom dans le fichier et afficher ces renseignements.

    Voilà qui termine le cours qur les fichiers.
Cet article est la propriété de discase. La copie et la diffusion sont libres sauf dans un but lucratif sans accord explicite de l'auteur.