Les fichiers - Applications pratiques

@joekakone

Joseph Konka @joekakone

03/03/2026 · 15 min

Mettez en pratique vos compétences sur les fichiers en langage C à travers trois projets professionnels : un système de journalisation horodaté, un utilitaire de chiffrement de flux et un gestionnaire d'inventaire binaire persistant.

Les fichiers - Applications pratiques#

Dans cette leçon d'application, nous allons consolider l'ensemble des notions abordées sur les flux texte, les formats tabulaires et les fichiers binaires à travers trois réalisations logicielles concrètes issues du monde de l'ingénierie logicielle.


Application 1 : Système de journalisation applicative (Logger C)#

Énoncé du problème#

Dans toute application d'entreprise ou serveur en production, il est impératif de consigner les événements clés (connexions, erreurs, transactions) dans un fichier journal (log file).

Concevez un module de journalisation offrant : 1. Trois niveaux de sévérité : INFO, WARNING, ERROR. 2. L'horodatage automatique de chaque entrée au format ISO [YYYY-MM-DD HH:MM:SS]. 3. L'écriture en mode ajout ("a") pour ne jamais écraser l'historique existant. 4. Une fonction de consultation des logs filtrée par niveau d'alerte.

Solution complète en C#

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

typedef enum
{
    LOG_INFO,
    LOG_WARNING,
    LOG_ERROR
} LogLevel;

const char *NiveauVersChaine(LogLevel niveau)
{
    switch (niveau)
    {
        case LOG_INFO:    return "INFO";
        case LOG_WARNING: return "WARN";
        case LOG_ERROR:   return "ERROR";
        default:          return "DEBUG";
    }
}

// Enregistre un message horodaté dans le journal
void EnregistrerLog(const char *chemin_log, LogLevel niveau, const char *message)
{
    FILE *fichier = fopen(chemin_log, "a");
    if (fichier == NULL)
    {
        perror("Impossible d'ouvrir le fichier de log");
        return;
    }

    // Récupération du temps système
    time_t maintenant = time(NULL);
    struct tm *temps_local = localtime(&maintenant);

    char horodatage[32];
    strftime(horodatage, sizeof(horodatage), "%Y-%m-%d %H:%M:%S", temps_local);

    // Écriture formatée dans le fichier journal
    fprintf(fichier, "[%s] [%-5s] %s\n", horodatage, NiveauVersChaine(niveau), message);

    fclose(fichier);
}

// Lit et affiche le journal en filtrant optionnellement par niveau
void AfficherLogs(const char *chemin_log, const char *filtre_niveau)
{
    FILE *fichier = fopen(chemin_log, "r");
    if (fichier == NULL)
    {
        printf("Aucun journal disponible a l'adresse '%s'.\n", chemin_log);
        return;
    }

    char ligne[256];
    printf("\n=== JOURNAL DES EVENEMENTS (Filtre: %s) ===\n",
           filtre_niveau ? filtre_niveau : "TOUS");

    while (fgets(ligne, sizeof(ligne), fichier) != NULL)
    {
        if (filtre_niveau == NULL || strstr(ligne, filtre_niveau) != NULL)
        {
            printf("%s", ligne);
        }
    }
    printf("===========================================\n\n");

    fclose(fichier);
}

int main(void)
{
    const char *journal = "application.log";

    // Simulation de plusieurs événements d'exécution
    EnregistrerLog(journal, LOG_INFO, "Demarrage du service d'authentification.");
    EnregistrerLog(journal, LOG_INFO, "Connexion de l'utilisateur 'admin'.");
    EnregistrerLog(journal, LOG_WARNING, "Temps de reponse eleve detecte sur la base SQL.");
    EnregistrerLog(journal, LOG_ERROR, "Echec de connexion au serveur de paiement (Timeout).");
    EnregistrerLog(journal, LOG_INFO, "Arret propre du service.");

    // Affichage complet
    AfficherLogs(journal, NULL);

    // Affichage des erreurs uniquement
    AfficherLogs(journal, "ERROR");

    return EXIT_SUCCESS;
}

Application 2 : Outil de chiffrement de flux de fichiers (Chiffrement XOR)#

Énoncé du problème#

Développez un utilitaire en ligne de commande capable de chiffrer ou déchiffrer un fichier quelconque (texte, binaire, image) en appliquant l'opération logique XOR avec une clé secrète.

L'opération XOR présente une propriété mathématique remarquable : $\((A \oplus K) \oplus K = A\)$ Le même programme sert donc à la fois pour le chiffrement et le déchiffrement.

Solution complète en C#

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

int TransformerFichierXOR(const char *source, const char *destination, const char *cle)
{
    FILE *f_in = fopen(source, "rb");
    if (f_in == NULL)
    {
        perror("Fichier source introuvable");
        return 0;
    }

    FILE *f_out = fopen(destination, "wb");
    if (f_out == NULL)
    {
        perror("Impossible de creer le fichier destination");
        fclose(f_in);
        return 0;
    }

    size_t longueur_cle = strlen(cle);
    size_t index_cle = 0;
    int octet;
    unsigned long total_octets = 0;

    // Traitement octet par octet pour préserver l'exactitude binaire
    while ((octet = fgetc(f_in)) != EOF)
    {
        int octet_chiffre = octet ^ (unsigned char)cle[index_cle];
        fputc(octet_chiffre, f_out);

        index_cle = (index_cle + 1) % longueur_cle;
        total_octets++;
    }

    fclose(f_in);
    fclose(f_out);

    printf("Transformation terminee : %lu octets traites avec succes.\n", total_octets);
    return 1;
}

int main(void)
{
    const char *texte_clair = "secret.txt";
    const char *texte_chiffre = "secret.enc";
    const char *texte_restaure = "secret_restaure.txt";
    const char *cle = "ALGOJUNGLE2026";

    // 1. Création d'un fichier source
    FILE *f = fopen(texte_clair, "w");
    if (f != NULL)
    {
        fprintf(f, "Informations strictement confidentielles - Projet C\n");
        fprintf(f, "Cle de voute de l'architecture securisee.\n");
        fclose(f);
    }

    // 2. Chiffrement
    printf(">>> Chiffrement en cours...\n");
    TransformerFichierXOR(texte_clair, texte_chiffre, cle);

    // 3. Déchiffrement
    printf(">>> Dechiffrement en cours...\n");
    TransformerFichierXOR(texte_chiffre, texte_restaure, cle);

    // 4. Vérification
    printf("\nContenu du fichier restaure :\n");
    FILE *res = fopen(texte_restaure, "r");
    if (res != NULL)
    {
        char buffer[256];
        while (fgets(buffer, sizeof(buffer), res) != NULL)
        {
            printf("%s", buffer);
        }
        fclose(res);
    }

    return EXIT_SUCCESS;
}

Application 3 : Système de gestion d'inventaire persistant (CRUD binaire)#

Énoncé du problème#

Créez une application complète de gestion des stocks d'un magasin d'informatique. Les données doivent être stockées dans un fichier binaire inventaire.dat. L'application doit proposer : 1. L'ajout d'un nouvel article (code, designation, prix_ht, quantite). 2. La consultation de tous les articles avec calcul de la valeur globale du stock. 3. La recherche d'un article par son code unique. 4. L'approvisionnement ou la vente d'un article avec mise à jour directe sur disque via fseek().

Solution complète en C#

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

#define CHEMIN_BD "inventaire.dat"

typedef struct
{
    int code;
    char designation[40];
    float prix_ht;
    int quantite;
} Article;

// Ajoute un article à la fin du fichier
void AjouterArticle(void)
{
    FILE *fichier = fopen(CHEMIN_BD, "ab");
    if (fichier == NULL)
    {
        perror("Erreur d'ouverture du fichier");
        return;
    }

    Article a;
    printf("\n--- AJOUT D'UN NOUVEL ARTICLE ---\n");
    printf("Code numerique : ");
    scanf("%d", &a.code);

    printf("Designation    : ");
    scanf(" %39[^\n]", a.designation);

    printf("Prix HT (EUR)  : ");
    scanf("%f", &a.prix_ht);

    printf("Quantite stock : ");
    scanf("%d", &a.quantite);

    fwrite(&a, sizeof(Article), 1, fichier);
    fclose(fichier);

    printf(">>> Article '%s' ajoute avec succes.\n\n", a.designation);
}

// Affiche la totalité des articles en stock
void ListerArticles(void)
{
    FILE *fichier = fopen(CHEMIN_BD, "rb");
    if (fichier == NULL)
    {
        printf("Aucun inventaire existant.\n");
        return;
    }

    Article a;
    float valeur_totale = 0.0f;
    int total_articles = 0;

    printf("\n=================================================================\n");
    printf("%-8s | %-25s | %-12s | %-8s | %s\n",
           "Code", "Designation", "Prix HT", "Stock", "Valeur Totale");
    printf("=================================================================\n");

    while (fread(&a, sizeof(Article), 1, fichier) == 1)
    {
        float val = a.prix_ht * a.quantite;
        valeur_totale += val;
        total_articles++;

        printf("%-8d | %-25s | %9.2f EUR | %8d | %9.2f EUR\n",
               a.code, a.designation, a.prix_ht, a.quantite, val);
    }

    printf("=================================================================\n");
    printf("Total references : %d  |  Valeur marchande globale : %.2f EUR\n\n",
           total_articles, valeur_totale);

    fclose(fichier);
}

// Met à jour la quantité en stock d'un article par accès direct (fseek)
void ModifierStock(int code_recherche, int quantite_variation)
{
    FILE *fichier = fopen(CHEMIN_BD, "r+b");
    if (fichier == NULL)
    {
        perror("Impossible d'ouvrir l'inventaire");
        return;
    }

    Article a;
    int trouve = 0;
    long position = 0;

    while (fread(&a, sizeof(Article), 1, fichier) == 1)
    {
        if (a.code == code_recherche)
        {
            trouve = 1;

            if (a.quantite + quantite_variation < 0)
            {
                printf("Erreur : stock insuffisant (%d disponibles).\n", a.quantite);
                break;
            }

            a.quantite += quantite_variation;

            // Repositionnement à l'adresse de début de cet enregistrement
            fseek(fichier, position, SEEK_SET);

            // Réécriture in-place
            fwrite(&a, sizeof(Article), 1, fichier);
            printf(">>> Stock du produit #%d mis a jour : %d unites.\n", a.code, a.quantite);
            break;
        }

        // Mémoriser la position avant la lecture du prochain article
        position = ftell(fichier);
    }

    if (!trouve)
    {
        printf("Article avec le code %d introuvable.\n", code_recherche);
    }

    fclose(fichier);
}

int main(void)
{
    // Réinitialisation de test pour la démonstration
    remove(CHEMIN_BD);

    // Initialisation directe de quelques articles
    FILE *init = fopen(CHEMIN_BD, "wb");
    if (init != NULL)
    {
        Article lot[3] = {
            {5001, "Clavier Mecanique RGB", 89.90f, 15},
            {5002, "Souris Sans Fil 16000 DPI", 49.50f, 30},
            {5003, "Ecran 27 Pouces 144Hz", 249.00f, 8}
        };
        fwrite(lot, sizeof(Article), 3, init);
        fclose(init);
    }

    // 1. Liste initiale
    ListerArticles();

    // 2. Vente de 5 claviers (variation: -5)
    printf("Vente de 5 claviers (Code 5001)...\n");
    ModifierStock(5001, -5);

    // 3. Réapprovisionnement de 10 écrans (variation: +10)
    printf("Reapprovisionnement de 10 ecrans (Code 5003)...\n");
    ModifierStock(5003, 10);

    // 4. Liste finale mise à jour
    ListerArticles();

    return EXIT_SUCCESS;
}

Auto-évaluation et synthèse#

1. Pourquoi est-il indispensable d'utiliser le mode binaire "rb" ou "wb" pour fseek et ftell fiables ? Sous certains systèmes d'exploitation (notamment Windows), les flux texte convertissent automatiquement les sauts de ligne \n en séquences de deux octets \r\n. Cette conversion altère le décompte exact d'octets retourné par ftell() et fausse les calculs de déplacement de fseek(). Le mode binaire garantit un rapport d'un pour un entre la position demandée et la réalité physique du fichier.
2. Que se passe-t-il si un programme se termine brutalement sans appeler fclose() ? Les données récemment écrites peuvent encore résider dans le tampon de mémoire vive (buffer interne de FILE *) sans avoir été physiquement écrites sur le disque dur. De plus, le descripteur de fichier reste verrouillé au niveau du système d'exploitation jusqu'à ce que le ramasse-miettes de l'OS intervienne.

Commentaires

Inscrivez-vous à une formation complète

Voir le catalogue