Chapitre 8 gestion fichiers texte et binaires en langage c p
Télécharger PDFChapitre 8 : Les fichiers
Ce chapitre explore la gestion des fichiers en programmation C, abordant l'introduction, l'ouverture, la fermeture, la lecture, l'écriture et les différents modes d'accès aux fichiers, ainsi que des exercices pratiques.
Introduction aux Fichiers
Il existe deux types principaux de fichiers que l'on manipule en programmation :
-
Fichiers Texte
Ce sont des fichiers dont le contenu représente uniquement une suite de caractères imprimables, d'espaces et de retours à la ligne (par exemple, les fichiers .txt). Ils peuvent être lus directement par un éditeur de texte.
-
Fichiers Binaires
Ce sont des fichiers qui ne sont pas assimilables à des fichiers textes (par exemple, les fichiers .exe, .mp3, .png). Ils ne peuvent pas être lus directement par un éditeur de texte.
Ouverture et Fermeture d'un Fichier
Avant toute opération de lecture ou d'écriture, un fichier doit être ouvert. Une fois les opérations terminées, il doit être fermé pour libérer les ressources.
Modes d'Ouverture d'un Fichier
L'ouverture d'un fichier se fait généralement avec la fonction fopen(), qui prend en paramètres le nom du fichier et le mode d'ouverture. Les modes courants incluent :
"r": Lecture seule. Le fichier doit exister."w": Écriture seule. Crée le fichier s'il n'existe pas, ou écrase son contenu s'il existe."a": Ajout (append). Ouvre le fichier pour écrire à la fin. Crée le fichier s'il n'existe pas."rb","wb","ab": Modes binaires correspondants pour la lecture, l'écriture et l'ajout."r+","w+","a+": Modes permettant la lecture et l'écriture simultanées.
Exemple d'Ouverture et Fermeture
Voici un exemple illustrant l'ouverture et la fermeture d'un fichier :
FILE *f;
// Tentative d'ouverture du fichier en mode lecture
f = fopen("mon_fichier.txt", "r");
if (f == NULL) {
printf("Impossible d'ouvrir le fichier.\\n");
} else {
// Le fichier est ouvert avec succès, des opérations peuvent être effectuées ici
printf("Le fichier a été ouvert avec succès.\\n");
fclose(f); // Fermeture du fichier
printf("Le fichier a été fermé.\\n");
}
Il est impératif de toujours vérifier la valeur de retour de fopen() pour s'assurer que l'ouverture a réussi (elle retourne NULL en cas d'échec) et de toujours fermer le fichier avec fclose().
Lecture et Écriture dans un Fichier
Une fois le fichier ouvert, le langage C permet plusieurs types d'accès à son contenu :
- Par caractère
- Par ligne
- Par données formatées
- Par enregistrement
- Accès direct
Accès par Caractère
Cette méthode permet de lire ou d'écrire un seul caractère à la fois dans le fichier, à l'aide des fonctions fgetc() et fputc().
char c = fgetc(fichier); // Lit un caractère
fputc('X', fichier); // Écrit le caractère 'X'
Accès par Ligne
On peut accéder au contenu du fichier ligne par ligne, en considérant les lignes comme des chaînes de caractères dans les fichiers texte.
Lecture par Ligne : fgets()
La fonction char *fgets(char *S, int max, FILE *f) permet de lire une chaîne de caractères.
S: Chaîne de caractères où sera stockée la ligne lue.max: Nombre maximum de caractères à lire. La lecture s'arrête au caractère'\n'(retour à la ligne) ou àmax-1caractères. Il est courant d'utilisersizeof(S)pour la taille du tampon.f: Descripteur du fichier.
La fonction fgets() lit jusqu'à max-1 caractères du fichier ou jusqu'à un retour à la ligne ou la fin de fichier. Elle stocke le résultat dans la chaîne S et ajoute le caractère nul '\0'. Elle retourne le pointeur S reçu en paramètre en cas de succès, ou NULL si la fin du fichier est atteinte ou si une erreur survient.
Écriture par Ligne : fputs()
Pour écrire une ligne de texte dans un fichier, on utilise la fonction :
int fputs(const char *S, FILE *f)
Cette fonction écrit la chaîne de caractères S dans le fichier de descripteur f. Elle retourne un nombre non négatif en cas de succès, ou EOF en cas d'erreur. La fonction n'ajoute pas automatiquement de caractère de retour à la ligne.
Exemple de lecture ligne par ligne
FILE *f;
char chaine[80];
f = fopen("exemple.txt", "r");
if (f == NULL) {
printf("Impossible d'ouvrir le fichier.\\n");
} else {
printf("Contenu du fichier :\\n");
while (fgets(chaine, sizeof(chaine), f) != NULL) {
printf("%s", chaine); // puts(chaine) aurait le même effet, ajoutant un retour à la ligne
}
fclose(f);
}
Accès par Données Formatées
On peut aussi lire et écrire des variables de types quelconques en utilisant fprintf() et fscanf(). Ces fonctions réalisent le même travail que printf() et scanf(), mais sur des fichiers ouverts en mode texte :
fprintf(FILE *f, const char *format, ...): Écrit des données formatées dans le fichierf.fscanf(FILE *f, const char *format, ...): Lit des données formatées depuis le fichierf.
Ces fonctions sont très utiles pour la lecture et l'écriture de données structurées de manière lisible.
Accès par Enregistrement (Binaire)
Cet accès permet de lire ou d'écrire des objets de type structure. Le fichier doit être ouvert en mode binaire (ex: "rb" pour lecture binaire, "wb" pour écriture binaire).
Les données échangées ne sont pas traitées comme du texte. Les fonctions principales sont :
size_t fread(void *bloc, size_t taille, size_t nb, FILE *f)size_t fwrite(const void *bloc, size_t taille, size_t nb, FILE *f)
Les paramètres sont décrits comme suit :
bloc: Adresse de l'espace mémoire (un pointeur) qui reçoit (pourfread) ou fournit (pourfwrite) les données de l'enregistrement. Cet espace mémoire doit être de taille suffisante.taille: Taille d'un enregistrement en nombre d'octets (ex:sizeof(MaStructure)).nb: Nombre d'enregistrements à lire ou à écrire.f: Descripteur du fichier.
Ces deux fonctions retournent le nombre d'enregistrements lus ou écrits avec succès.
Accès Direct (fseek)
L'accès direct permet de se positionner à un endroit précis dans un fichier. La fonction int fseek(FILE *f, long offset, int origine) déplace le pointeur de fichier.
f: Descripteur du fichier.offset: Nombre d'octets à décaler à partir de l'origine.origine: Point de référence (SEEK_SETpour le début,SEEK_CURpour la position actuelle,SEEK_ENDpour la fin).
La fonction fseek retourne 0 s'il n'y a pas de problème, sinon une valeur différente de zéro en cas d'erreur.
Détection de la Fin de Fichier (feof)
La fonction int feof(FILE *f) détecte si la fin du fichier f a été atteinte. Elle retourne une valeur différente de zéro si la fin de fichier est détectée, et zéro sinon. Il est important de noter que feof() ne devient vrai qu'après une tentative de lecture qui a échoué en raison de la fin du fichier.
Exercices
Exercice 1 : Compter l'occurrence d'un caractère
Écrire un programme C qui calcule et affiche le nombre d'occurrences d'un caractère saisi au clavier dans un fichier texte dont on saisit le nom.
Solution :
#include <stdio.h>
void main() {
char c, nom_fich[20];
int nb = 0;
FILE *f;
printf("Donnez le nom du fichier : ");
// ATTENTION: gets() est dangereuse et doit être évitée. Préférer fgets().
gets(nom_fich);
f = fopen(nom_fich, "r");
if (f != NULL) {
printf("Taper le caractère à chercher : ");
scanf(" %c", &c); // L'espace avant %c consomme les caractères blancs résiduels
// ATTENTION: L'utilisation de feof() comme condition de boucle est souvent incorrecte.
// Il est préférable de vérifier la valeur de retour de la fonction de lecture.
int lu_char;
while ((lu_char = fgetc(f)) != EOF) {
if (lu_char == c) {
nb++;
}
}
printf("Le caractère '%c' se trouve %d fois dans le fichier %s\\n", c, nb, nom_fich);
fclose(f);
} else {
printf("Impossible d'ouvrir le fichier %s.\\n", nom_fich);
}
}
Exercice 2 : Création d'un fichier binaire de réels
Écrire un programme C qui crée un fichier binaire de nom reels.dat, puis enregistre N réels saisis au clavier dans ce fichier.
Solution :
#include <stdio.h>
void main() {
float x;
int N, i;
FILE *f;
f = fopen("reels.dat", "wb"); // Ouverture en mode écriture binaire
if (f == NULL) {
printf("Erreur : Impossible de créer le fichier reels.dat.\\n");
return;
}
printf("Taper le nombre de réels à sauvegarder : ");
scanf("%d", &N);
for (i = 0; i < N; i++) { // Boucle pour saisir N réels
printf("Taper un réel : ");
scanf("%f", &x);
fwrite(&x, sizeof(float), 1, f); // Écriture du réel dans le fichier binaire
}
fclose(f);
printf("Fichier reels.dat créé et rempli avec %d réels.\\n", N);
}
Exercice 3 : Calcul de la moyenne des réels d'un fichier binaire
Écrire un programme C qui calcule et affiche la moyenne des nombres réels stockés dans le fichier reels.dat créé dans l'exercice précédent.
Solution :
#include <stdio.h>
void main() {
float x, s = 0;
int count = 0;
FILE *f;
f = fopen("reels.dat", "rb"); // Ouverture en mode lecture binaire
if (f == NULL) {
printf("Erreur : Impossible d'ouvrir le fichier reels.dat.\\n");
return;
}
// Lecture des réels tant que fread retourne 1 (un élément lu avec succès)
while (fread(&x, sizeof(float), 1, f) == 1) {
s = s + x;
count++;
}
if (count > 0) {
printf("La somme des réels du fichier reels.dat est : %f\\n", s);
printf("La moyenne des réels est : %f\\n", s / count);
} else {
printf("Le fichier reels.dat est vide ou n'a pas pu être lu.\\n");
}
fclose(f);
}
Exercice 4 : Gestion de Fichiers d'Étudiants
On définit des étudiants par un nom, un prénom et un code (deux étudiants différents ne peuvent pas avoir le même code). Écrire en C les fonctions suivantes :
CreeFichier: qui permet de saisir le nom d'un fichier, un entier N ainsi que les noms, prénoms et codes des N étudiants pour construire le fichier.AfficheFichier: qui liste le contenu d'un fichier dont le nom est donné en paramètre.
#include <stdio.h>
#include <stdlib.h> // Pour exit (optionnel, utilisé dans des gestionnaires d'erreurs plus robustes)
#include <string.h> // Pour la manipulation de chaînes si nécessaire
typedef struct {
int code;
char nom[20];
char prenom[20];
} ETUDIANT;
void CreeFichier(char *nom_fichier) {
FILE *fp;
ETUDIANT e;
int n, i;
fp = fopen(nom_fichier, "wb"); // Ouverture en mode écriture binaire
if (fp == NULL) {
printf("Impossible de créer le fichier %s.\\n", nom_fichier);
return;
}
printf("Donner le nombre des étudiants : ");
scanf("%d", &n);
// Vider le buffer d'entrée après scanf
int c;
while ((c = getchar()) != '\n' && c != EOF);
for (i = 0; i < n; i++) {
printf("\\nSaisie de l'étudiant %d :\\n", i + 1);
printf("Code : ");
scanf("%d", &e.code);
printf("Nom : ");
// ATTENTION: scanf("%s") est dangereux car il ne vérifie pas la taille du buffer.
// Préférer fgets() pour une lecture sécurisée de chaînes.
scanf("%s", e.nom);
printf("Prénom : ");
scanf("%s", e.prenom);
// Vider le buffer d'entrée après scanf pour la prochaine itération
while ((c = getchar()) != '\n' && c != EOF);
fwrite(&e, sizeof(ETUDIANT), 1, fp);
}
fclose(fp);
printf("\\nFichier %s créé avec %d étudiants.\\n", nom_fichier, n);
}
void AfficheFichier(char *nom_fichier) {
FILE *fp;
ETUDIANT e;
fp = fopen(nom_fichier, "rb"); // Ouverture en mode lecture binaire
if (fp == NULL) {
printf("Impossible d'ouvrir le fichier %s.\\n", nom_fichier);
return;
}
printf("\\nContenu du fichier %s :\\n", nom_fichier);
while (fread(&e, sizeof(ETUDIANT), 1, fp) == 1) { // Lire tant qu'un enregistrement est lu avec succès
printf("Code: %d, Nom: %s, Prénom: %s\\n", e.code, e.nom, e.prenom);
}
fclose(fp);
}
void main() {
char nom_fic_etudiants[20];
printf("Entrez le nom du fichier pour les étudiants (ex: etudiants.dat) : ");
// ATTENTION: gets() est dangereuse et doit être évitée. Préférer fgets().
gets(nom_fic_etudiants);
CreeFichier(nom_fic_etudiants);
AfficheFichier(nom_fic_etudiants);
}
Foire Aux Questions (FAQ)
Q1 : Quelle est la principale différence entre un fichier texte et un fichier binaire ?
Un fichier texte contient des caractères qui sont directement lisibles et interprétables par l'humain à l'aide d'un éditeur de texte standard. En revanche, un fichier binaire stocke des données sous leur forme brute, souvent optimisée pour la machine, et n'est pas directement intelligible sans un programme capable d'interpréter sa structure spécifique.
Q2 : Pourquoi est-il crucial de fermer un fichier après utilisation ?
Fermer un fichier avec fclose() est essentiel pour plusieurs raisons. Cela libère les ressources système allouées au fichier, garantit que toutes les données écrites en mémoire tampon sont effectivement enregistrées sur le disque, et évite la corruption de données ou les fuites de ressources qui pourraient entraîner des problèmes de performance ou des erreurs dans le programme.
Q3 : Quand doit-on utiliser les fonctions fread() et fwrite() ?
Les fonctions fread() et fwrite() sont principalement utilisées pour la lecture et l'écriture de données dans des fichiers binaires. Elles sont idéales pour manipuler des blocs de données de taille fixe, comme des structures ou des tableaux, car elles transfèrent les données directement entre la mémoire vive et le fichier sans conversion de format.