Année universitaire 2002-2003
espace

Licence d'informatique
Module 4 - partie "C / shell"



Travaux dirigés 6 :

  • 1. Les différents types de fichiers.
  • 2. Avantages comparés des différents types de fichiers.
  • 3. Lecture dans un fichier binaire : fonction fread.
  • 4. Écriture dans un fichier binaire : fonction fwrite.
  • 5. Positionnement dans un fichier : fonction fseek.
  • 6. Test de fin de fichier : fonction feof.
  • 7. Sortie d'un programme : fonction exit.
  • 8. Entrée standard et sorties standard d'une commande.
  • 9. Exercice 1 : recopie d'un fichier.
  • 10. Exercice 2 : lecture de blocs d'octets dans un fichier.

  • 1. Les différents types de fichiers.

    On a déjà vu, dans la séance 2 de travaux pratiques, qu'il existe deux catégories de fichiers, et ce indépendamment de tout langage de programmation :

    Pour savoir si un fichier est un fichier de texte ou un fichier binaire, un moyen simple est de l'éditer à l'aide d'un éditeur de texte (par exemple asedit) :

    Exemples :

    Remarque :

    Tout fichier, de texte ou binaire, est composé d'octets, mais une même information est codée différemment dans un fichier de texte ou dans un fichier binaire.

    Exemple :

    Par conséquent :

    Remarque :

    Pour effectuer des lectures dans un fichier de texte à l'aide de la fonction fscanf, on ouvrira ce fichier sous le mode "rt". Pour effectuer des écritures dans un fichier de texte à l'aide de la fonction fprintf, on ouvrira ce fichier sous les modes "wt" (c'est-à-dire qu'on écrasera l'ancien contenu de ce fichier) ou "at" (c'est-à-dire que les écritures seront concaténées à l'ancien contenu de ce fichier). Pour effectuer des lectures dans un fichier binaire à l'aide de la fonction fread, on ouvrira ce fichier sous le mode "rb". Enfin, pour effectuer des écritures dans un fichier binaire à l'aide de la fonction fwrite, on ouvrira ce fichier sous les modes "wb" (écriture en début de fichier) ou "ab" (écriture en fin de fichier). Là encore, ces règles peuvent être transgressées, mais il peut s'ensuivre des erreurs difficiles à corriger.

    Pour effectuer des lectures dans un fichier image au format ppm, qui comporte une partie de texte et une partie binaire, suivant qu'on manipule l'une ou l'autre partie, on utilisera les fonctions fscanf ou fread, et donc on ouvrira le fichier sous le mode "rt" ou sous le mode "rb" (cf. la séance 6 de travaux pratiques).

    2. Avantages comparés des différents types de fichiers.

    On peut se demander la raison pour laquelle il existe plusieurs types de fichiers. Plutôt que de disserter sur leur nécessaire coexistence, on se contentera ici de citer quelques avantages comparés de ces deux types de fichiers dans le cas de valeurs numériques (comme par exemple les images), puisque des valeurs numériques peuvent être stockées, a priori, sous forme de caractères (les chiffres plus le caractère .) ou sous forme binaire :

    3. Lecture dans un fichier binaire : fonction fread.

    La lecture d'un bloc d'octets dans un fichier binaire s'effectue à l'aide de la fonction fread, d'en-tête :

    où :

    La fonction fread renvoie le nombre d'informations effectivement lues. Ce nombre d'informations peut être inférieur au nombre requis, dans le cas où la fin du fichier est atteinte.

    4. Écriture dans un fichier binaire : fonction fwrite.

    L'écriture d'un bloc d'octets dans un fichier binaire s'effectue à l'aide de la fonction fwrite, d'en-tête :

    La fonction fwrite renvoie le nombre d'informations effectivement écrites. Ce nombre d'informations peut être inférieur au nombre requis, dans le cas où l'écriture est effectuée sur un disque saturé.

    Exemple :

    #include <stdio.h>
    #define N 256

    int main(void)
    {
    FILE *f1,*f2; /* Identificateurs de fichiers. */
    char mat[N][N];

    if ((f1=fopen("image.brt","rb"))==NULL)
    printf("Fichier original introuvable.\n");
    else
    {
    if (fread(mat,1,N*N,f1)<N*N)
    {
    printf("Manque de données dans le fichier original.\n");
    fclose(f1);
    }
    else
    {
    if ((f2=fopen("image_copie.brt","wb"))==NULL)
    {
    printf("Création du fichier copie impossible.\n");
    fclose(f1);
    }
    else
    {
    if (fwrite(mat,1,N*N,f2)<N*N)
    {
    printf("Erreur à l'écriture.\n");
    fclose(f1);
    fclose(f2);
    }
    else
    {
    printf("Copie terminée. Tout s'est bien passé.\n");
    fclose(f1);
    fclose(f2);
    }
    }
    }
    }
    return(0);
    }

    5. Positionnement dans un fichier : fonction fseek.

    Il peut être utile d'accéder à un fichier de manière directe ("par accès direct"), pour y effectuer des lectures ou des écritures, mais il faut alors gérer explicitement le pointeur d'octets qui donne la "position courante" dans le fichier. Cela est possible, grâce à la fonction fseek, d'en-tête :

    où :

    La fonction fseek renvoie 0 si tout s'est bien passé et -1 sinon, en particulier dans le cas où on essaierait de revenir en arrière avant le début d'un fichier. Néanmoins, lorsqu'on essaie d'avancer au-delà de la fin d'un fichier, la valeur retournée par fseek vaut 0 et non pas -1, comme on pourrait s'y attendre (tout se passe comme si on "piétinait" à la fin du fichier).

    Remarque :

    6. Test de fin de fichier : fonction feof.

    La fonction feof, d'en-tête :

    renvoie une valeur non nulle si la fin du fichier (d'identificateur id_fich) est atteinte, et 0 sinon.

    Remarque :

    Attention :

    7. Sortie d'un programme : fonction exit.

    L'arrêt de l'exécution d'un programme s'effectue à l'aide d'un appel à la fonction exit, d'en-tête :

    Par convention, il est conseillé de retourner la valeur 0 lorsque tout s'est bien passé, et une valeur entière non nulle lorsqu'il y a eu un problème. Cette valeur s'appelle le "code de retour". Il ne faut pas confondre l'appel exit(code_retour); avec les instructions break; (cette instruction permet de sortir d'un bloc), return(valeur); ou return valeur; (ces instructions, placées dans une fonction, permettent de sortir de cette fonction, en renvoyant la valeur valeur non forcément entière, sans arrêt de l'exécution du programme). En revanche, placées dans le programme principal main, les instructions return(code_retour); ou return code_retour; provoquent l'arrêt du programme, ainsi que le renvoi de l'entier code_retour (sauf dans le cas où la fonction main est appelée récursivement).

    Remarque :

    Exemple :

    #include <stdio.h>
    #include <stdlib.h>
    #define N 256

    int main(void)
    {
    FILE *f1,*f2; /* Identificateurs de fichiers. */
    char mat[N][N];

    if ((f1=fopen("image.brt","rb"))==NULL)
    {
    printf("Fichier original introuvable.\n");
    exit(1);
    }
    else
    {
    if (fread(mat,1,N*N,f1)<N*N)
    {
    printf("Manque de données dans le fichier original.\n");
    fclose(f1);
    exit(2);
    }
    else
    {
    if ((f2=fopen("image_copie.brt","wb"))==NULL)
    {
    printf("Création du fichier copie impossible.\n");
    fclose(f1);
    exit(3);
    }
    else
    {
    if (fwrite(mat,1,N*N,f2)<N*N)
    {
    printf("Erreur à l'écriture.\n");
    fclose(f1);
    fclose(f2);
    exit(4);
    }
    else
    {
    printf("Copie terminée. Tout s'est bien passé.\n");
    fclose(f1);
    fclose(f2);
    exit(0);
    }
    }
    }
    }
    }

    Remarques :

    8. Entrée standard et sorties standard d'une commande.

    Un programme exécutable est l'équivalent d'une commande UNIX (un certain nombre de commandes UNIX ont déjà été énumérées lors de la première séance de travaux pratiques). Les noms stdin, stdout, stderr désignent respectivement "l'entrée standard", la "sortie standard" et la "sortie standard des erreurs" d'une commande. Par défaut, l'entrée standard est associée au clavier, la sortie standard et la sortie standard des erreurs sont associées à l'écran. Il n'y a donc pas besoin d'utiliser la fonction fopen pour pouvoir lire des caractères tapés au clavier ou afficher des caractères à l'écran.

    À l'intérieur d'un programme, pour effectuer une lecture sur l'entrée standard, on écrit (en supposant que a est une variable de type int) :

    À l'intérieur d'un programme, pour effectuer une écriture sur la sortie standard, on écrit :

    À l'intérieur d'un programme, pour effectuer une écriture sur la sortie standard des erreurs, on écrit :

    On peut "rediriger" l'entrée standard, la sortie standard ou la sortie standard des erreurs d'une commande, et donc en particulier d'un programme, sur des fichiers. La redirection de l'entrée standard de la commande nom_commande sur le fichier de nom f_entree se fait grâce à la syntaxe suivante, au moment du lancement de cette commande :

    > nom_commande < f_entree

    La redirection de la sortie standard de la commande nom_commande sur le fichier de nom f_sortie se fait grâce à la syntaxe suivante :

    > nom_commande > f_sortie

    La redirection de la sortie standard des erreurs de la commande nom_commande sur le fichier de nom f_erreurs se fait grâce à une syntaxe qui dépend du shell utilisé. Avec le shell par défaut sur marine, qui est tcsh, c'est :

    > nom_commande >& f_erreurs

    En revanche, avec le "shell de Bourne", qui seul sera étudié dans le cadre du module 4, la syntaxe est la suivante (remarquer le nouveau prompt, caractéristique du shell de Bourne) :

    $ nom_commande 2> f_erreurs

    Remarque :

    9. Exercice 1.

    Écrire un programme permettant de recopier le fichier fich1 dans un fichier fich2, par paquets de 512 octets (sauf, le cas échéant, pour le dernier paquet).

    Exemple d'appel du programme exécutable :

    > recopie < fich1 > fich2

    On codera les différents types d'erreurs de la façon suivante :

    Il est conseillé d'utiliser l'algorithme suivant, écrit en pseudo-langage, dans lequel les codes de retour ne sont pas précisés :

    tant qu'on peut lire 512 octets :
    {
    écrire 512 octets ;
    si écriture impossible, alors sortir ;
    }
    s'il n'y a plus d'octets à écrire, alors sortir ;
    sinon :
    {
    écrire les octets restants ;
    si écriture impossible, alors sortir ;
    sinon, sortir ;
    }

    Dans chaque cas, on fera afficher un message explicite sur la sortie standard des erreurs.

    10. Exercice 2.

    1) Écrire une fonction paquet, permettant de lire n octets dans un fichier d'identificateur id_fich à partir d'une position pos (on suppose que ce fichier a déjà été ouvert en lecture dans le programme principal), et de les stocker dans un tableau tab :

    On sortira de cette fonction avec :

    2) Écrire le programme principal appelant cette fonction.


    Ces pages ont été réalisées par A. Crouzil, J.D. Durou et Ph. Joly.
    Pour tout commentaire, envoyer un mail à crouzil@irit.fr, à durou@irit.fr ou à Philippe.Joly@irit.fr.