Lecture et écriture de fichiers texte

@joekakone

Joseph Konka @joekakone

03/03/2026 · 12 min

Maîtrisez les fonctions d'entrée et sortie pour les fichiers texte en C : fgetc, fputc, fgets, fputs, fprintf et fscanf. Évitez les pièges classiques comme la mauvaise utilisation de feof().

Lecture et écriture de fichiers texte#

Les fichiers texte constituent le format d'échange le plus universel en informatique. Qu'il s'agisse de scripts de configuration, de journaux d'erreurs (logs) ou de rapports tabulaires, savoir lire et écrire du texte formaté est une compétence essentielle pour tout développeur C.

La bibliothèque standard <stdio.h> propose trois niveaux de granularité pour interagir avec les fichiers texte : 1. Caractère par caractère : fgetc() et fputc() 2. Ligne par ligne : fgets() et fputs() 3. Formaté : fscanf() et fprintf()


1. Ouverture et fermeture sécurisées#

Avant toute opération de lecture ou d'écriture, le fichier doit être ouvert via la fonction fopen(). Si le fichier demandé n'existe pas (en lecture) ou si l'utilisateur ne dispose pas des droits d'accès nécessaires, fopen() renvoie le pointeur nul NULL.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main(void)
{
    FILE *fichier = fopen("donnees.txt", "r");

    if (fichier == NULL)
    {
        // perror() affiche la description humaine de l'erreur système
        perror("Impossible d'ouvrir donnees.txt");
        return EXIT_FAILURE;
    }

    // Traitement du fichier...

    fclose(fichier);
    return EXIT_SUCCESS;
}

[!IMPORTANT] Ne tentez jamais d'appeler fclose() ou une fonction de lecture sur un pointeur FILE * égal à NULL. Cela provoquerait un crash immédiat du programme (Segmentation Fault).


2. Manipulation caractère par caractère : fgetc et fputc#

Ces fonctions sont idéales pour les opérations légères, le comptage brut ou le transcodage de texte.

  • int fputc(int c, FILE *stream) : écrit le caractère c dans le flux.
  • int fgetc(FILE *stream) : lit le caractère suivant et avance le curseur d'un octet. Renvoie la constante EOF (End Of File, généralement -1) en fin de fichier ou en cas d'erreur.

Le piège du type de retour de fgetc#

[!WARNING] La fonction fgetc() renvoie un int et non un char ! En C, un char non signé ne peut pas représenter la valeur négative de EOF (-1). Si vous stockez le résultat de fgetc() dans une variable de type char, la condition de fin de fichier risque de ne jamais être détectée, provoquant une boucle infinie.

// Exemple : Copie d'un fichier caractère par caractère
#include <stdio.h>
#include <stdlib.h>

void CopierFichier(const char *source, const char *destination)
{
    FILE *src = fopen(source, "r");
    if (src == NULL)
    {
        perror("Erreur ouverture source");
        return;
    }

    FILE *dest = fopen(destination, "w");
    if (dest == NULL)
    {
        perror("Erreur creation destination");
        fclose(src);
        return;
    }

    int c; // OBLIGATOIREMENT int pour comparer correctement avec EOF
    while ((c = fgetc(src)) != EOF)
    {
        fputc(c, dest);
    }

    fclose(src);
    fclose(dest);
    printf("Copie terminee avec succes.\n");
}

3. Manipulation ligne par ligne : fgets et fputs#

Pour les fichiers textuels structurés en lignes (logs, listes de noms), la lecture ligne par ligne est bien plus performante et naturelle.

  • char *fgets(char *str, int num, FILE *stream) : lit au plus num - 1 caractères depuis le flux et les stocke dans str. La lecture s'interrompt dès qu'un saut de ligne (\n) est rencontré ou que la fin de fichier est atteinte. fgets ajoute toujours le caractère de fin de chaîne '\0'.
  • int fputs(const char *str, FILE *stream) : écrit la chaîne str dans le flux (sans ajouter automatiquement de saut de ligne, contrairement à puts).

Nettoyage du saut de ligne résiduel#

fgets() conserve le caractère \n dans le tampon si l'espace le permet. Pour le supprimer proprement :

#include <string.h>

void SupprimerSautLigne(char *chaine)
{
    chaine[strcspn(chaine, "\r\n")] = '\0';
}

Exemple complet de lecture ligne par ligne#

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TAILLE_BUFFER 256

int main(void)
{
    FILE *fichier = fopen("citations.txt", "r");
    if (fichier == NULL)
    {
        perror("Erreur lors de l'ouverture");
        return EXIT_FAILURE;
    }

    char ligne[TAILLE_BUFFER];
    int numero_ligne = 1;

    // fgets retourne NULL lorsque la fin du fichier est atteinte
    while (fgets(ligne, sizeof(ligne), fichier) != NULL)
    {
        // Suppression du retour chariot
        ligne[strcspn(ligne, "\r\n")] = '\0';

        printf("[%03d] %s\n", numero_ligne, ligne);
        numero_ligne++;
    }

    fclose(fichier);
    return EXIT_SUCCESS;
}

4. Entrées/Sorties formatées : fprintf et fscanf#

Ces fonctions fonctionnent exactement comme leurs homologues de console printf et scanf, avec un premier paramètre supplémentaire désignant le pointeur FILE *.

Écriture formatée : fprintf#

FILE *rapport = fopen("rapport.txt", "w");
if (rapport != NULL)
{
    char nom[] = "Alice";
    float note = 17.5f;

    fprintf(rapport, "%-15s | %5.2f / 20\n", nom, note);
    fclose(rapport);
}

Lecture formatée : fscanf et son piège#

fscanf tente de faire correspondre le contenu du fichier avec une chaîne de format. Elle renvoie le nombre d'éléments correctement convertis et assignés.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE *fichier = fopen("notes.txt", "r");
    if (fichier == NULL)
    {
        perror("Fichier introuvable");
        return EXIT_FAILURE;
    }

    char nom[50];
    float note;

    // fscanf retourne le nombre de conversions réussies (ici 2)
    while (fscanf(fichier, "%49s %f", nom, &note) == 2)
    {
        printf("Etudiant : %-15s - Note : %0.2f\n", nom, note);
    }

    fclose(fichier);
    return EXIT_SUCCESS;
}

5. Le piège classique de l'anti-pattern feof()#

Beaucoup de débutants écrivent par réflexe :

// ATTENTION : CODE INCORRECT (Anti-pattern)
while (!feof(fichier))
{
    fgets(buffer, sizeof(buffer), fichier);
    printf("%s", buffer); // La dernière ligne sera affichée en double !
}

Pourquoi ce code est-il faux ?#

En C, l'indicateur de fin de fichier feof() ne passe à vrai qu'après qu'une opération de lecture a échoué en essayant de lire au-delà du dernier octet. Par conséquent, lors de la dernière itération : 1. feof() est encore faux. 2. fgets() tente de lire, rencontre la fin de fichier et échoue (en laissant l'ancien contenu dans le tampon). 3. Le programme continue et affiche le tampon précédent une seconde fois !

flowchart TD
    A[Test !feof : Vrai car pas encore lu au-delà] --> B[Tentative de lecture fgets : Échec EOF]
    B --> C[Affichage du buffer : Doublon de la dernière ligne !]
    C --> D[Test feof au tour suivant : Vrai, sortie tardive]

[!TIP] La bonne pratique : Testez toujours directement la valeur de retour de la fonction de lecture dans la condition de votre boucle : - while ((c = fgetc(fichier)) != EOF) - while (fgets(buffer, sizeof(buffer), fichier) != NULL) - while (fscanf(fichier, "...", ...) == NB_ATTENDU)


6. Mise en pratique : Analyseur statistique de texte#

Voici un programme complet qui lit un fichier texte, compte le nombre de lignes, de mots et de caractères, et affiche un rapport récapitulatif.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdbool.h>

typedef struct
{
    unsigned long lignes;
    unsigned long mots;
    unsigned long caracteres;
} StatistiquesTexte;

StatistiquesTexte AnalyserFichier(const char *chemin)
{
    StatistiquesTexte stats = {0, 0, 0};
    FILE *fichier = fopen(chemin, "r");

    if (fichier == NULL)
    {
        perror("Impossible d'ouvrir le fichier pour analyse");
        return stats;
    }

    int c;
    bool dans_un_mot = false;

    while ((c = fgetc(fichier)) != EOF)
    {
        stats.caracteres++;

        if (c == '\n')
        {
            stats.lignes++;
        }

        // Détection des délimitations de mots
        if (isspace(c))
        {
            dans_un_mot = false;
        }
        else if (!dans_un_mot)
        {
            dans_un_mot = true;
            stats.mots++;
        }
    }

    // Si le fichier n'est pas vide et ne se termine pas par \n, compter la dernière ligne
    if (stats.caracteres > 0 && c != '\n')
    {
        stats.lignes++;
    }

    fclose(fichier);
    return stats;
}

int main(void)
{
    const char *chemin = "texte.txt";

    // Création d'un fichier de test
    FILE *test = fopen(chemin, "w");
    if (test != NULL)
    {
        fprintf(test, "Le langage C est puissant et concis.\n");
        fprintf(test, "Il constitue le fondement des systemes d'exploitation modernes.\n");
        fprintf(test, "Apprenez a dompter ses pointeurs et ses flux de donnees.\n");
        fclose(test);
    }

    StatistiquesTexte resultat = AnalyserFichier(chemin);

    printf("==========================================\n");
    printf("   RAPPORT D'ANALYSE : %s\n", chemin);
    printf("==========================================\n");
    printf("  Nombre total de caracteres : %lu\n", resultat.caracteres);
    printf("  Nombre total de mots       : %lu\n", resultat.mots);
    printf("  Nombre total de lignes     : %lu\n", resultat.lignes);
    printf("==========================================\n");

    return EXIT_SUCCESS;
}

7. Défi d'application#

Écrivez un programme nommé convertisseur_maj.c qui prend en entrée un fichier source et produit un fichier cible où toutes les lettres minuscules sont converties en majuscules (en utilisant la fonction toupper() de <ctype.h>), tout en conservant scrupuleusement la ponctuation et les retours à la ligne.

Commentaires

Inscrivez-vous à une formation complète

Voir le catalogue