Le contenu d'un fichier est rendu disponible par le système de fichiers seulement après qu'il ait été ouvert. Lorsque le fichier n'est plus utile, il est important de penser à le fermer, car il y a une limite au nombre de fichiers qui peuvent rester ouverts en même temps.
#include <stdio.h> int main(void) { FILE* flux; /* pointeur vers le flux */ flux = fopen("exemple.s", "w"); /* ouverture du flux */ if (flux) { ... fclose(flux); /* fermeture du flux */ } return EXIT_SUCCESS; }
Nous allons utiliser les fonctions de stdio, qui traitent les fichiers comme des flux. Un flux est une source ou une destination d'octets, ce qui peut s'appliquer aussi bien aux fichiers qu'aux communications réseaux ou aux tuyaux.
La fonction fopen a pour prototype :
FILE * fopen (const char *nom_du_fichier, const char *type_ouverture);
Le premier paramètre est le nom du fichier sous la forme d'une chaîne de caractère. Ce nom peut être précédé d'un chemin absolu ou relatif. Dans le cas d'un chemin relatif, le point de départ sera le répertoire où se situe l'exécutable. Le séparateur entre les noms des sous-répertoires peut être '\\' ou '/'.
Le second paramètre permet de choisir le type d'ouverture demandé, sous la forme d'une chaîne de caractère. Pour l'instant, seuls trois types nous intéressent :
Remarque Tenter d'ouvrir en lecture un fichier qui n'existe pas est impossible. Tenter d'ouvrir en écriture un fichier qui n'existe pas provoque la création de ce fichier (si possible).
Si l'ouverture s'avère impossible, la fonction renvoie NULL. Sinon, elle renvoie l'adresse du flux. Ce flux est un enregistrement où se trouvent entre autres le descripteur du fichier, l'indicateur de position, ainsi que les tampons de lecture et d'écriture. La forme exacte de cet enregistrement peut varier d'une version à l'autre de la bibliothèque standard ; on n'accède donc jamais directement à son contenu.
La fonction fclose a pour prototype :
int fclose (FILE* flux);
Le paramètre est l'adresse du flux qui doit être fermé. En cas de fermeture réussie, le résultat sera 0. Dans le cas contraire, le résultat sera égal à la constante EOF. Cette constante est obligatoirement négative (elle vaut -1 sous Linux).
Le plus souvent un flux est parcouru du début à la fin. Pour se souvenir de la progression dans ce parcours, le flux contient un indicateur de position. Toute opération de lecture fait avancer cette position : de cette façon, deux lectures successives ne lisent pas deux fois la même chose mais lisent des données qui se suivent dans le flux.
La fonction de lecture la plus élémentaire consiste à lire un certain nombre de blocs d'octets dans le flux et à les placer en mémoire : c'est la fonction fread.
size_t fread (void *zone, size_t taille, size_t nombre, FILE *flux);
Le premier argument est l'adresse de la zone mémoire qui va recevoir les données. Le deuxième est la taille en octets d'un bloc. le troisième est le nombre de blocs à lire. Le dernier est l'adresse du flux de lecture. Le résultat est le nombre de blocs lus, il devrait donc être égal à nombre quand tout se passe bien.
Si tout ne s'est pas bien passé, soit il y a eu une erreur de lecture, soit nous avons tout simplement atteint la fin du fichier. Les fonctions feof et ferror nous aident à faire la différence.
int feof(FILE* flux); int ferror(FILE* flux);
feof renvoie vrai si et seulement si la fin du fichier est atteinte. Toute opération de lecture dans cet état n'aboutira pas (il n'y a plus rien à lire). ferror renvoie vrai si et seulement si une précédente opération a rencontré une erreur. Les opérations suivantes peuvent aboutir ou pas, suivant que le problème persiste.
Remarque Si fread ne lit pas assez de blocs, il est possible que ni feof ni ferror ne renvoient vrai. Cela signifie alors qu'il reste trop peu d'octets dans le flux pour former un bloc complet.
La fonction d'écriture la plus élémentaire consiste à copier un certain nombre de blocs d'octets depuis la mémoire vers le flux : c'est la fonction fwrite.
size_t fwrite (void *zone, size_t taille, size_t nombre, FILE *flux);
La fonction fwrite est l'exact inverse de fread et elle marche de façon similaire.
Si tout ne s'est pas bien passé, il y a forcément eu une erreur d'écriture. En effet, en écriture séquentielle on est toujours placé en fin de fichier, et chaque opération d'écriture rallonge simplement le fichier.
Remarque Une opération d'écriture ne provoque pas nécessairement une mise à jour immédiate du fichier. Pour gagner du temps, les données à écrire sont accumulées dans un tampon, et ne sont vraiment transmises que lorsque le tampon est plein ou lorsque le fichier est (proprement) fermé.
Records. Le fichier top10 contient la liste des dix meilleurs scores d'un jeu video ainsi que le sigle des joueurs qui les ont obtenus. Les scores sont des entiers au format int little-endian. Les sigles sont formés de trois caractères ASCII tous imprimables. Dans le fichier il y a d'abord le score du meilleur joueur, puis son sigle, puis le score du second meilleur joueur, puis son sigle, et ainsi de suite.
Écrivez un programme qui lit ce fichier puis affiche le «Hall of Fame».
bob@box:~$ ./a.out 004867123 BOB 004854739 <O> 002587684 \o/ 002483729 KIX 001982491 :-) 001444726 ALF 000758162 LOL 000758161 T_T 000048214 GG 000000001 @_@
Challenger. Écrivez un programme qui prend en argument sur la ligne de commande un score et un nom et qui met à jour le fichier top10 de l'exercice précédent.
bob@box:~$ ./a.out 1581427 '>_>' 004867123 BOB 004854739 <O> 002587684 \o/ 002483729 KIX 001982491 :-) 001581427 >_> 001444726 ALF 000758162 LOL 000758161 T_T 000048214 GG
Copie. Écrivez un programme qui copie un fichier sur le modèle de la commande cp.
bob@box:~$ ./a.out cute_overload.gif chaton.gif
Cette commande doit créer un nouveau fichier nommé chaton.gif dont le contenu soit identique à celui de cute_overload.gif.
bob@box:~$ ./a.out -a part2.txt part1.txt
Cette commande doit ajouter le contenu du fichier part2.txt à la fin du contenu préexistant dans le fichier part1.txt.
Chiffrement. Écrivez un programme qui permette de chiffrer/déchiffrer un fichier par la méthode suivante :
Rectangles. Récupérez les fonctions du cinquième exercice du sujet sur les structures et écrivez avec un programme qui permet à son utilisateur de dessiner autant de rectangles qu'il le souhaite, les uns par dessus les autres. En appuyant sur une touche, on termine le programme, mais à la prochaine exécution, tous les rectangles dessinés précédemment devront être encore là.