Vous êtes sur la page 1sur 26

3.

FICHIERS

Stocker données dans des fichiers pour pouvoir les utiliser plus tard.

C distingue 2 catégories de fichiers : fichiers standards et systèmes.

On distingue : fichiers de textes et fichiers binaires.

Fichiers binaires : fichiers de nombres, ils ne sont pas listables.

Fichiers textes (ASCII) sont listables, le dernier octet est EOF.

La plupart des fonctions ou primitives permettant la manipulation


des fichiers sont rangés dans la bibliothèque standard string.h.

1
3.1. Définition et propriétés

Un fichier est un ensemble structuré de données stocké sur un support externe.


Un fichier séquentiel est représenté séquentiellement par la suite ordonnée de ses
éléments, notée : <x1, x2, x3,…,xn>.
Propriétés de fichiers séquentiels :
- les fichiers se trouvent en état d’écriture ou bien en état de lecture.
- On accède uniquement à un seul enregistrement se trouvant en face de la tête de L/ E.
- Après chaque accès, la tête de L/ E est déplacée derrière la donnée lue en dernier lieu.
- Un fichier peut être vide.

Un sous-fichier est la restriction d’un fichier f à un intervalle P (suite d’éléments


consécutifs). P est un ensemble fini totalement ordonné.
Ex : f = <5, 7, 59, 67, -1, 0, 18> alors <5, 7, 59> et <67, -1, 0, 18> sont des sous-
fichiers de f. Par contre, <5, 59, 18> n’est pas un sous-fichier.

Tout fichier peut être schématisé par un ruban défilant devant une tête de L/ E.
Elément accessible ou élément courant est l’ élément en face de la tête de L/ E.
Un fichier étant une suite finie, on introduit une marque sur le ruban notée -| (fin de
fichier).
2
tête de lecture/ écriture

sens de défilement

x1 x2 x3 x4 …. xn -|

-----f- -----------------------f+----------------
L’élément courant (x3) permet, par définition, de
décomposer un fichier en deux sous-fichiers : f = f- || f+
Où f- est le sous-fichier déjà lu et f+ est le sous-fichier
qui reste à lire. L’élément courant est le premier élément
de f+.
Dans cet exemple : f- = <x1, x2>, f+ = <x3,…,xn>,
l’élément courant est x4.
Notation f—est définie par : f- = f--|| val.
f—est le fichier f- avant exécution de l’action : lire(f,val)

3
3.2. Actions primitives d’accès
Elles donnent un modèle du comportement dynamique d’un fichier.

3.2.1. Déclaration
Etapes pour traiter un fichier :
- déclarer un pointeur de type FILE*,
- affecter l’adresse retournée par fopen à ce pointeur,
- employer le pointeur à la place du nom du fichier dans toutes les
instructions de lecture ou d’écriture,
-libérer le pointeur à la fin du traitement par fclose.

3.2.2. Primitives d’accès


Avant de créer ou de lire un fichier, nous devons l’ouvrir.
Après avoir terminé la manipulation du fichier, nous devons le fermer.

4
a) Ouvrir un fichier séquentiel
FILE * fopen(char *nom, char *mode) ;
On passe 2 chaînes de caractères :
nom : celui figurant sur le disque, par exemple toto.dat.
mode : r lecture seule ;
w écriture seule, destruction de l’ancienne si elle existe.
a+ lecture et écriture d’un fichier existant (mise à jour), pas de
création d’une nouvelle version, le pointeur est positionné à la fin du
fichier.

b) Créer un fichier séquentiel


int fprintf(FILE *f, char * format, liste d’expression) ;
Retourne EOF en cas d’erreur.
exemple : fprintf(f, %s\n, il fait beau) ; elle crée un fichier.

Avant de lire un fichier on doit l’ouvrir en lecture.


5
c) Lire un fichier séquentiel

int fgetc(FILE *f); lit un caractère mais retourne un entier ; retourne


EOF si erreur, le pointeur avance d’une case.
Exemple : char c;
c = (char)fgetc(f);

int fscanf(FILE *f, char *format, liste d’adresses );

Avant de quitter un fichier on doit le fermer.

d) Fermer un fichier séquentiel

fclose(f); f est un pointeur du type FILE*


Exemple : fclose(f);

6
e) Détecter la fin du fichier

Les fonctions d’ouverture, de lecture et d’écriture de fichier donnent une


valeur à FEOF qui détecte la fin d’un fichier.
La fonction feof(f) retourne la valeur true, si la tête de lecture du fichier
est arrivé à la fin du fichier ; sinon elle délivre la valeur false.
<f> est un pointeur du type FILE* qui est relié au nom du fichier à lire.
Exemple : une boucle typique pour lire les enregistrements d’un fichier
séquentiel référencé par un pointeur f peut avoir la forme suivante :
FILE* f;
char nom[25], prenom[15];
f=fopen(“toto.dat”, “r”);

while(!feof(f))
{ fscanf(f,  %s %s\n , nom, prenom);
traiter(nom, prenom);

7
}
CH_3_1.C : Le C lit et affiche le fichier C:\AUTOEXEC. BAT en le
parcourant caractère par caractère.

#include<stdio.h>
#include<stdlib.h>
main()
{ FILE *f ; // char car ; …
f = fopen(C:\\AUTOEXEC.BAT, r);
if(!f)
{ printf(Impossible d’ouvrir le fichier\n);
exit(-1) ;
}
while(!feof(f))putchar(fgetc(f)); /* ou fscanf(f,”%c”, &car);
fclose(f) ; printf("%c", car);
} */
8
Tableau 1 : Résumé sur fichier caractère et texte
Action C
ouverture en écriture f = fopen(nom, w)
ouverture en lecture f = fopen(nom,r)
fermeture fclose(f)
fct finfichier feof(f)
écriture fprintf(f,…, expression)
fputc

lecture fscanf(f,…, adresse)


c=getc(f)

9
e) Détruire un fichier
int remove(char *nom) ;
retourne 0 si la fermeture s’est bien passée.
Exemple : remove(c:\\toto.dat) ;

f) Renommer un fichier
int rename(char *oldName, char *newName) ;
retourne 0 si la fermeture s’est bien passée.

g) positionner le pointeur au début du fichier


void rewind(FILE * f) ;

10
3,3. Algorithmes traitant un seul fichier

3.3.1. Somme des éléments d’un fichier


Fonction sommeElement calcule la somme des éléments d’un fichier.

int sommeElement (char nomf[])


{ int som, val ; FILE* f;
f=fopen(nomfich, "r") ;
fscanf(f, "%d", &som);
while(!feof(f))
{ fscanf(f, "%d", &val);
som += val;
}
return som;
}
11
Remarque : cas d’un fichier vide, on doit écrire un autre plus général à
condition de préciser la convention adoptée dans ce cas.

Convention : la somme des éléments d’un fichier vide est égale à zéro.
On exécute, à l’initialisation, les actions suivantes :
f=fopen(nomfich, "r") ; s=0;
La suite des algorithmes reste inchangée.

3.3.2. Recherche de la valeur maximale contenue dans un fichier


Soit un fichier f non vide, tel que l’ensemble des valeurs soit muni d’une
relation d’ordre total.
On cherche une valeur maxf et telle que yf, on ait maxy. Une telle
valeur est dite maximale.

L’en-tête de la fonction est le suivant :


int maximum(char nomf[])
12
int maximum(char nomf[])
{ int max, val; FILE * f;
f=fopen(nomf, "r"); fscanf(f, "%d", &max);
while(!feof(f))
{ fscanf(f, "%d", &val);
if(max<val)max=val;
}
return max;
}
Pour les algorithmes précédents, l’itération est toujours de la forme :
f=fopen(nomf, "r");

while(!feof(f))
{ fscanf(f, "%d", &val); traiter(val);
}
Où traiter est une fonction quelconque agissant ou non sur le contenu de
val. A chaque exécution de l’action fscanf(f, "%d", &val); le nombre
d’éléments de f+ diminue de 1. 13
3.3.3. Accès à un élément d’un fichier
Ce problème est habituellement formulé de 2 manières :
 Soit on connaît le rang k de l’élément et on recherche alors le kième
élément du fichier,
 Soit on connaît la valeur de l’élément que l’on cherche et on recherche
le premier élément qui correspond à cette valeur (la première
occurrence)
On parle alors de recherche ou d’accès :
 Par position dans le premier cas,
 Par valeur ou associatif dans le second.

3.3.3.1. Accès par position (accès au kième élément)


L’en-tête de la fonction int accesk(char nomf[], int k, booleen *trouve)
On peut décomposer le fichier en deux f1k-1 et fkn, d’où : f = f1k-1 || fkn
Pour que le kième existe, le sous-fichier fkn ne doit pas être vide (fkn <>).
Dans ce cas, et si on a effectué la lecture des k-1 premiers éléments, f+
commence par le kième élément du fichier f. 14
L’algorithme est alors composé de 2 parties :
 Parcours des k-1 premiers éléments de f (f-= f1k-1)
 Ensuite, suivant que le fichier f+ est vide ou non, on aura les résultats
suivants :
** f+ est vide, trouve prendra la valeur faux (0) et valk sera indéfinie.
** f+ n’est pas vide, trouve prendra la valeur vrai et valk contiendra la
valeur du kième élément en effectuant l’action fscanf( f, "%d", &valk) ;

a) construction de f- = f1k-1
f=fopen(nomfich, "r");
i=1;
while((i<k) && (!feof(f)))
{ fscanf(f, "%d", &val);
i+=1;
}

On a obtenu la 1ière partie de l’algorithme. 15


b) Suite de l’algorithme

A la sortie whileon a ((i < k), (feof(f)) d’après la loi de De Morgan


(i k) v feof(f).

i > k signifie que k  0.


En effet, i est initialisé avec la valeur 1. D’autre part i augmente de 1 à
l’intérieur de while.
Si k>0, la négation de i<k ne peut être que i=k. On ne peut avoir i>k
sans être passé au préalable par la valeur de i égale à celle de k.
Donc i>k ne peut jamais se produire.

Nous allons étudier tous les cas possibles de l’assertion (i==k)||feof(f).

16
• i == k, feof(f)
On est à la fin de fichier, et on a trouvé que le rang de l’élément était égal à k. Le
fichier a donc k-1 éléments, le sous-fichier f+ est vide et le kième élément n’existe pas : f
= f1k-1, trouve prend la valeur false et valk est indéfinie.

•i == k, !feof(f)
signifie qu’on a trouvé le kième élément sans atteindre la fin du fichier.
On a donc : (f- = f1i-1, i = k)  f- = f1k-1
Il s’ensuit que f+ = f1n avec k  n ; fscanf(f, "%d", &valk) permet à valk de prendre
la valeur du kième élément et trouve doit prendre la valeur true.

•i < k, feof(f)
On a parcouru tous les éléments du fichier sans trouver le kième élément.
f- = f1n, f+ est vide, trouve prend la valeur false et valk est indéfinie.

•i < k, !feof(f)
Ce cas ne peut jamais se produire car l’expression (i=k)||((feof(f)) est fausse et n’est
pas donc une assertion.

On peut résumer ces 4 cas dans un tableau appelé tableau de sortie.


17
Tableau 2 : tableau de sortie de tantque

i == k feof(f) résultat
vrai vrai trouve = false
vrai faux trouve=true; fscanf(f, "%d", &valk)
faux (i<k) vrai trouve = false
faux (i<k) faux impossible (à cause de while)

En examinant les 3 premières lignes du tableau, on constate que la valeur de trouve est
identique è celle de !feof(f). Il faut donc continuer l’algorithme en utilisant un test sur
la valeur du prédicat.

Par contre, utiliser un test sur la valeur de i==k, entraîne une erreur dans le cas de la
1ière ligne. On aurait donc écrit un algorithme qui aurait fonctionné dans la plupart des
cas, sauf si par hasard on avait rencontré le cas d’un fichier contenant k-1 éléments.

D’où l’intérêt de la construction et de l’analyse du tableau de sortie qui permet de


compléter l’algorithme.
18
On poursuit l’algorithme en écrivant :
if(!feof(f))
{ trouve=true;
fscanf(f, "%d", &valk); return valk;
}
else trouve = false;
ou encore
trouve = !feof(f);
if(trouve)
{ fscanf(f, "%d", &valk) ; return valk;
}
Une autre solution consiste à initialiser trouve à false au début de l’algorithme, il suffit
alors de terminer l’algorithme avec :
trouve=false;
if(!feof(f))
{ trouve=true;
fscanf(f, "%d", &valk); return valk;
}
C’est cette dernière solution que nous utilisons fréquemment, car elle permet d’alléger
l’algorithme en évitant l’écriture de else.
19
Algorithme d’accès au kième élément.

int accesk(char nomf[], int k, booléen *trouve)


{ int nomb, valk, i; FILE *f;
f=fopen(nomfich, "r");
*trouve = false; i=1;
while((i<k) && (!feof(f))
{ fscanf(f, "%d", &nomb);
i+=1;
}
if(!feof(f))
{ *trouve = true;
fscanf(f, "%d", &valk);
return valk;
}
}
20
3.3.3.2. Accès associatif
Soit un fichier f et une valeur val de même type que les éléments de f.
On veut savoir si la valeur contenue dans val est présente dans le fichier
f (= f1n).
valf signifie que i  [1..n], xi = val.

booléen acces(char nomf[], int val)


{ int nomb ; booléen egal; FILE *f;
f=fopen(nomfich, "r"); egal=false;
while(!feof(f) && !egal)
{ fscanf(f, "%d", &nomb);
if(nomb==val)egal=true;
}
return egal;
}

21
Tableau 3 : Tableau de sortie

fdf(f) egalRésultat : val  f


vrai vrai trouve = true (val = Df)
vrai fauxtrouve = false
fauxvrai trouve = true
fauxfauximpossible (while)

On constate que trouve a la même valeur que egal.

3.3.4. Fichier ordonné (fichier trié)

Si tous les éléments consécutifs d’un fichier vérifient la relation d’ordre


(xi  xi+1), on dit que le fichier est trié par ordre croissant.
On appellera fichier trié (ou ordonné) un fichier ordonné par ordre
croissant.
On admettra que le fichier vide et que le fichier ne contenant qu’un seul
élément sont triés. 22
En d’autres termes, la définition d’un fichier trié :
 un fichier vide est trié,
 un fichier comportant un seul élément est trié,
 soit f = <x1, x2, …,xn>, avec n>1, le fichier f est trié si i  [1..n-1], la
relation xi  xi+1 est vraie.

On peut donner une définition récursive d’un fichier trié :


 un fichier vide est trié,
 un fichier contenant un seul élément est trié,
 (f1i trié, xi  xi+1) f1i+1 trié pour i  [1..n-1].

3.3.4.1. Algorithme vérifiant qu’un fichier est trié.

Ecrire un algorithme qui vérifie si un fichier est trié ou non. L’en-tête est
le suivant : booléen trie(char nomf[]).

23
Algorithme vérifiant qu’un fichier est trié.

booléen trie(char nomf[])


{ int precedent, nomb; FILE* f;
f=fopen(nomf, "r");
if(feof(f)) return true;
else
{ fscanf(f, "%d", &precedent);
nomb=precedent;
while(!feof(f) && (precedent nomb))
{ precedent=nomb; fscanf(f, "%d", &nomb) ;
}
return precedentnomb;
}
}

24
3.3.4.2. Algorithme d’accès à un élément dans un fichier trié

En général, l’accès associatif dans un fichier ordonné est plus rapide que
dans le cas d’un fichier non ordonné.
On cherche à écrire une fonction dont l’en-tête est :
booléen accest(char nomf[], int val)

On peut toujours décomposer un fichier f trié et non vide en 2 sous-


fichiers f’ et f’’ tel que : f = f’ || f’’, f’< val  f’’.
Remarque
Si f’ est vide, la relation s’écrit : val  f’’
Si f’’ est vide, la relation s’écrit f’ < val.

On contrôle l’itération par une variable booléenne infer définie par :


(infer, f- < val) || (!infer, Df-  val).

25
Algorithme

booléen accest(char nomf[], int val)


{ int nomb; boléen infer; FILE *f;
f=fopen(nomf, "r");
infer=true;
while(!feof(f) && infer)
{ fscanf(f, "%d", &nomb);
if(nomb>=val)infer=false;
}
if(nomb==val)return true;
else return false;
}

26

Vous aimerez peut-être aussi