Introduction au C

Le logo du C
Sommairestdio : Les entrées et les sorties

Lire et écrire des fichiers

Je vais considérer que vous savez ce que c'est qu'un fichier, un chemin, etc. et je vais juste expliquer comment les manipuler en C.

Pour information, la plupart des fonctions qu'on va voir commencent par un f pour file (« fichier »), leur « vrai » nom est en général ce qui suit.

Pour commencer, il faut « ouvrir » le fichier, avec la fonction fopen (qui vient de stdio.h). Cette fonction prend le chemin du fichier à ouvrir (relativement au dossier dans lequel on lance notre programme, ou de manière absolue si on le commence avec un /), et le mode d'ouverture (on va en reparler juste après). Elle retourne un FILE *, un pointeur vers un fichier, qu'on va pouvoir lire ou écrire.

Le mode est une chaîne de caractère indiquant comment ouvrir le fichier :

Il y a d'autres options mais ces trois là sont les plus importantes.

Ce programme ouvre texte.txt en écriture, en ajoutant les données à la fin du fichier :

#include <stdio.h>

int main() {
    FILE *fichier = fopen("texte.txt", "wa");

    return 0;
}

Comme cette fonction renvoie un pointeur, il peut potentiellement être nul. Ça arrivera quand le fichier ne pourra pas être ouvert, par exemple parce qu'on a pas les permissions suffisantes pour le lire. Il vaut mieux mettre une vérification après avoir ouvert un fichier, pour éviter d'avoir un programme qui plante :

FILE *fichier = fopen("texte.txt", "wa");
if (fichier == NULL) {
    printf("Erreur lors de l'ouverture de texte.txt\n"); // On affiche une erreur
    return 1; // Puis on quitte le programme, mais sans planter
}

// Ici on sait que fichier n'est pas nul, on peut l'utiliser sans souci

Les fonctions pour lire et écrire un fichier sont (respectivement) fscanf et fprintf. Elles ressemblent beaucoup à scanf et printf mais elles prennent un FILE * en premier argument, pour indiquer dans quel fichier lire ou écrire. À part ça, rien ne change par rapport à scanf et printf.

Il faut aussi savoir que quand on lit ou écrit dans un fichier, le programme se souvient d'où il en est, et on ne peut pas revenir en arrière (pour être honnête, on peut mais on ne verra pas comment faire ici, regardez la fonction fseek si vous êtes curieu⋅euse⋅x). Pour savoir si on a atteind la fin du fichier on peut utiliser feof (« EOF » signifie « end of file », « fin de fichier »).

Par exemple, on peut lire un fichier et afficher des étoiles à la place des a avec ce code :

#include <stdio.h>

int main() {
    // On ouvre le fichier (et on quitte avec une erreur si ça se passe mal)
    FILE *fichier = fopen("texte.txt", "r");
    if (fichier == NULL) {
        printf("Impossible d'ouvrir le fichier\n");
        return 1;
    }

    char c = ' ';
    while (!feof(fichier)) { // Tant qu'on est pas à la fin
        // On lit caractère par caractère
        fscanf(fichier, "%c", c);

        // On affiche le caractère (ou une étoile si c'est un « a »)
        if (c == 'a') {
            printf("*");
        } else {
            printf("%c", c);
        }
    }

    return 0;
}

Dernier point à savoir sur les fichiers : une fois qu'on a fini de les utiliser il faut utiliser fclose pour signaler au système d'exploitation qu'on ne va plus s'en servir. Il pourra alors laisser d'autres programmes utiliser ce même fichier.

Si on oublie de faire un fclose, le système d'exploitation attendra que notre programme se termine pour « fermer » le fichier. Ce n'est pas très grave quand on écrit des programmes courts comme ici, mais si on en fait qui tournent pendant plus longtemps, ça peut poser problème.

Bien-ŝur, une fois qu'on a fermé un fichier, on n'a plus le droit de lire ou d'écrire dedans.

Bonus : le terminal est un fichier

Les système d'exploitation UNIX ont une sorte d'obcession avec les fichiers, et ils ont tendance à tout voir comme des fichiers : votre processeur est un fichier, une connexion vers un site internet est un fichier, etc.

Et l'entrée et la sortie standard (en gros : le terminal et le clavier) des programmes sont aussi des fichiers ! C'est très pratique, parce qu'on peut écrire des programmes « indépendants » d'un nom de fichier.

Pour utiliser ces fichiers spéciaux, on n'utilise pas fopen, mais les constantes spéciales stdin (standard input, « entrée standard », le clavier) et stdout (standard output, « sortie standard », le texte du terminal). Ces constantes viennent aussi de stdio.h.

Ce programme par exemple ouvre un fichier si on en précise un dans ses arguments, sinon il utilise la sortie standard.

#include <stdio.h>

int main(int argc, char **argv) {
    // On essaie d'ouvrir le fichier si il a été précisé en argument
    FILE *fichier = NULL;
    if (argc > 1) {
        fichier = fopen(argv[1], "r");
    }
    
    // Si l'ouverture a raté ou s'il n'y avait juste pas d'argument,
    // on utilise stdin
    if (fichier == NULL) {
        fichier = stdin;
    }

    // On affiche le contenu du fichier
    // Si le fichier est stdin, ça va juste répéter
    // ce qu'on tape jusqu'à ce qu'on fasse Ctrl+D ou Ctrl+C.
    // On considère que le fichier ne contient que des nombres les uns
    // à la suite des autres.
    while(!feof(fichier)) {
        int x = 0;
        scanf(fichier, "%d", &x);

        printf("%d\n", x);
    }

    return 0;
}