17. Les fichiers textes

 

Un fichier texte est un ensemble de caractères se trouvant sur un support externe (donc pas en mémoire) et structuré au niveau logique en lignes de longueurs variables (au niveau physique, il y a simplement des caractères de fin de ligne).

 

Les deux traitements fondamentaux que l’on effectue sur les fichiers textes sont :

-          la lecture séquentielle : on lit les caractères ou les lignes dans l’ordre où ils se trouvent. Le fichier est alors un flot d’entrée du programme.

-          L’écriture séquentielle : on écrit des caractères ou des lignes en les plaçant bout à bout dans le fichier. Le fichier est alors un flot de sortie du programme.

 

En C, les définitions sur les fichiers se trouvent dans stdio.h

 

Le type FILE

 

En C, le type FILE est le type fichier qui est utilisé via des pointeurs.

 

FILE *fic ;

 

déclare un pointeur sur fichier.

 

Ouverture d’un fichier

 

La fonction fopen permet d’ouvrir un fichier, ce qui est nécessaire avant d’effectuer toute opération.

 

fopen(nomfic,mode)

 

nomfic est une chaîne de caractères qui est le nom du fichier pour le système d’exploitation ;

mode est une chaîne de caractères qui précise comment le fichier doit être ouvert ;

 

Le résultat est NULL si le fichier n’a pas pu être ouvert ; c’est un pointeur sur FILE s’il a été ouvert.

 

Les valeurs fondamentales de mode sont :

"r" : ouverture en lecture (read). Le fichier doit exister, son contenu n’est pas détruit, le descripteur se place au début. Seules les opérations de lecteur sont autorisées.

"w" : ouverture en écriture (write). Le fichier ne doit pas nécessairement exister. S’il existe, son contenu est détruit, le descripteur se place au début (le fichier est vide). Seules les opérations d’écriture sont autorisées.

"a" : ouverture en allongement (append). Le fichier ne doit pas nécessairement exister. S’il existe, son contenu n’est pas détruit, le descripteur se place à la fin. Seules les opérations d’écriture sont autorisées.

 

Sauf situation particulière, l’ouverture d’un fichier lui associe un tampon (buffer) qui est un espace de la mémoire contenant une partie des informations du fichier et ayant pour but d’accélérer les opérations effectuées sur le fichier.

 

Fermeture d’un fichier

 

La fonction fclose permet de fermer un fichier, ce qui est nécessaire quand on a terminé d’effectuer les opérations sur ce fichier.

 

close(fic)

 

fic est un pointeur sur fichier qui a été obtenu par un fopen n’ayant pas échoué.

 

La fonction rend 0 en cas de succès, une autre valeur en cas d’échec.

 

Ne pas fermer un fichier ouvert en lecture n’a en général pas d’effet.

Ne pas fermer un fichier ouvert en écriture peut faire perdre une partie, voire l’intégralité, du fichier.

 

Ecriture par fprintf

 

fprintf est analogue à printf avec un argument complémentaire, le premier, qui indique dans quel fichier on écrit.

 

fprintf(fic, format, e1, e2…)

 

écrit dans le fichier fic les valeurs des expressions e1, e2… avec le format format à la suite.

Rend le nombre de caractères écrits, un nombre négatif en cas d’erreur.

 

Schéma classique d’écriture d’un fichier ligne par ligne

 

FILE *fic;

 

fic = fopen("MonFichier.txt","w");

if (fic == NULL)

      printf("Le fichier MonFichier.txt n'a pas ete cree");

else

{

      while ( <test de continuité du traitement> )

      {

            < construction d’une chaîne ch à écrire dans le fichier

  sur une ligne >

                    

fprintf(fic,"%s\n",ch);

      }

      fclose(fic);

}

 

Lecture par fscanf

 

fscanf est analogue à scanf avec un argument complémentaire, le premier, qui indique dans quel fichier on lit.

 

fscanf(fic, format, &e1, &e2…)

 

lit depuis le fichier fic les valeurs suivantes et les affecte aux variables e1, e2… avec le format format.

 

Rend un entier qui est le nombre d’expressions lues ou EOF s’il y a eu erreur.

EOF signifie End Of File. En C, c’est un entier négatif.

 

Schéma classique de lecture d’un fichier ligne par ligne

 

FILE *fic;

 

fic = fopen("MonFichier.txt","r");

if (fic == NULL)

      printf("Le fichier MonFichier.txt n'a pas ete ouvert");

else

{

      while (1)

      {

            if (fscanf(fic,"%s",&ch) == EOF)

break;

            < traitement de ch >

      }

      fclose(fic);

}

 

à Attention : les espaces et les tabulations arrêtent la lecture (comme les fins de lignes). Si l’on veut une vraie lecture de ligne, prendre la fonction fgets.

 

#define NN 255

...

Char ch[NN] ;

...

      while (1)

      {

            if (fgets(ch, NN, fic) == NULL)

break;

            < traitement de ch >

      }

 

Les flots standard en C

 

En mode console, le clavier et l’écran sont des flots.

 

C possède 3 flots standard qui sont ouverts dès le lancement du programme et n’ont pas besoin d’être fermés :

 

stdin : standard input, en fonctionnement classique, c’est le clavier, pour lire des informations au clavier.

stdout : standard output, en fonctionnement classique, c’est l’écran, pour écrire des informations à l’écran.

stderr : standard error, en fonctionnement classique, c’est l’écran, pour écrire des messages d’erreur.

 

printf(format, e1, e2…)

est équivalent à :

fprintf(stdout, format, e1, e2…)

 

scanf(format, e1, e2…)

est équivalent à :

fscanf(stdin, format, e1, e2…)

 

En mode console, sur certains systèmes (Unix, MS-DOS) on peut, au lancement du programme, remplacer stdin par un fichier texte ou stdout par un fichier texte ou faire les deux.

 

monProgramme < fic1.txt

lance monProgramme avec fic1.txt à la place de stdin. Les lectures au clavier sont remplacées par des lectures dans fic1.

 

monProgramme > fic2.txt

lance monProgramme avec fic2.txt à la place de stdout. Les écritures à l’écran sont remplacées par des écritures dans fic2.

 

monProgramme < fic1.txt > fic2.txt

lance monProgramme avec fic1.txt à la place de stdin et fic2.txt à la place de stdout.

 

On peut enchaîner deux programmes, faire en sorte que les sorties de l’un (sur stdout) soient les entrées de l’autres (sur stdin), exemple :

 

prog1 | prog2

prog1 lit au clavier et écrit dans le flot d’entrée de prog2

prog2 lit dans le flot de sortie de prog1 et écrit à l’écran.

 

Autre exemple :

prog1 < fic1.txt | prog2 > fic2.txt

prog1 lit dans fic1.txt  et écrit dans le flot d’entrée de prog2

prog2 lit dans le flot de sortie de prog1 et écrit dans fic2.txt 

 

Autres fonctions d’entrées sorties

 

feof(fic)

Renvoie une valeur non nulle si l’on est en fin de fichier, 0 si l’on n’y est pas.

 

fputc(car,fic)

écrit le caractère car dans le fichier fic. Revoie le caractère écrit ou EOF s’il y a eu erreur.

 

putchar(car)

analogue a fputc pour stdout

 

fputs(ch,fic)

écrit la chaine ch dans le fichier fic. Revoie EOF s’il y a eu erreur, une valeur positive dans les autres cas. fputs ne met pas le caractère de fin de ligne, il faut le mettre soi-même. Il peut être plus facile d’utiliser fprintf pour cela.

 

puts(ch)

analogue a fputs pour stdout

 

fgetc(fic)

Revoie le caractère suivant de fic ou EOF s’il y a eu erreur ou si la fin du fichier est atteinte.

 

getchar()

analogue à fgetc pour stdin

 

fgets(s, n, fic)

Lecture d’une ligne de fic dans la chaîne s en se limitant à n-1 caractères.

Le caractère de fin de ligne '\n' est copié dans le tableau et un '\0' est mis ensuite.

Renvoie s ou NULL en  cas d’erreur ou de fin de fichier.

 

gets(s)

analogue à fgets pour stdin mais sans contrôle du nombre de caractères et en remplaçant la fin de ligne par '\0'.

 

sprintf(ch, format, &e1, &e2…)

analogue à fprintf mais le résultat est mis dans la chaîne ch

 

sscanf(ch, format, &e1, &e2…)

analogue à fscanf mais le résultat est extrait de la chaîne ch

 

Enregistrement au format csv simplifié

 

Cela consiste à écrire des structures à raison d’une par ligne, les champs étant séparés par des « ; ». Il faut qu’il n’y ait pas de « ; » et pas de « " » dans les champs à écrire.

 

Exemple d’écriture d’une liste de personnes

 

void EnregistrerLaListe(LISTE_PERSONNE l)

{

      FILE *fic;

      char nomfic[20];

 

      printf("\nNom du fichier : ");

      scanf("%s",&nomfic);

 

      fic = fopen(nomfic,"w");

      if (fic == NULL)

            printf("Le fichier %s n'a pas ete cree",nomfic);

      else

      {

            while (l != NULL)

            {

                  fprintf(fic,"%s;%s;%d\n",l->nom,l->prenom,l->age);

                  l = l->suivant;

            }

            fclose(fic);

      }

}

 

Exemple de lecture  d’une liste de personnes

 

Ici, on lit ce format ligne par ligne avec fgets et on extrait les champs de la chaîne avec une procédure que l’on écrit pour cela.

 

/* extrait de s les caractères allant jusqu'au premier ";"

   les place dans champ et les enlève de s

   rend champ */

void champ_suiv(char *s, char *champ)

{

      char *s1, *c1, *s2;

 

      /* on extrait */

      s1 = s;     /* pointe sur l'endroit à traiter */

      c1 = champ; /* pointe sur l'endroit à remplir */

      while (*s1 != '\0' && *s1 != ';')

      {

            if (*s1 >= ' ') /* on ne prend pas les caractères de contrôle */

                  *c1++ = *s1;

            s1++;

      }

      *c1 = '\0';

 

      /* on décale */

      if (*s1 == ';')

            s1++;

      s2 = s;   /* pointe sur l'endroit à remplir */

      while (*s1 != '\0')

      {

            if (*s1 >= ' ') /* on ne prend pas les caractères de contrôle */

                  *s2++ = *s1;

            s1++;

      }

      *s2 = '\0';

}

 

/* ----------------------------------------------------

On charge la liste à partir d'un fichier dont on saisit le nom

Il y a une information par ligne

S'il n'y a pas tous les champs pour une personne, on l'ignore

On rend la liste chargée

------------------------------------------------------*/

LISTE_PERSONNE ChargerListe()

{

      FILE *fic;

      char nomfic[20], ch[256], champ[256];

      LISTE_PERSONNE l, lp;

 

      lp = NULL;

      printf("\nNom du fichier : ");

      scanf("%s",&nomfic);

      fic = fopen(nomfic,"r");

      if (fic == NULL)

            printf("Le fichier %s n'a pas ete ouvert",nomfic);

      else

            {

            while (fgets(ch, 256, fic) != NULL)

            {

                  l = malloc(sizeof(CEL_LISTE_PERSONNE));

                  champ_suiv(ch,champ);

                  strcpy(l->nom,champ);

                  champ_suiv(ch,champ);

                  strcpy(l->prenom,champ);

                  champ_suiv(ch,champ);

                  l->age = atoi(champ);

                  l->suivant = lp;

                  lp = l;

            }

            fclose(fic);

            lp = inverse(lp);

      }

      return lp;

}

 

Exemple de lecture  d’une liste de personnes

 

Ici, on lit ce format champ par champ avec une fonction que l’on écrit et qui lit le fichier caractère par caractère.

 

/* ----------------------------------------------------

On lit un champ dans le fichier f

On rend le caractère d'arrêt de la lecture ';' ou '\n' ou EOF

------------------------------------------------------*/

char getchamp(FILE *f, char *champ, int n)

{

      char c;

      int i = 0;

 

      c = fgetc(f);

      while (c != EOF && c != '\n' && c != ';')

      {

         if (i < n-1)

               champ[i++] = c;

         c = fgetc(f);

      }

      champ[i] = '\0';

   return c;

}

 

/* ----------------------------------------------------

On lit jusqu'à renconter '\n' ou EOF

On rend le dermier caractère lu

------------------------------------------------------*/

char finirligne(FILE *f)

{

      char c;

      c = fgetc(f);

      while (c != EOF && c != '\n')

         c = fgetc(f);

   return c;

}

 

/* ----------------------------------------------------

On charge la liste à partir d'un fichier dont on saisit le nom

Il y a une personne par ligne, les champs sont séparés par des ";"

On lit par champ avec la fonction getchamp

------------------------------------------------------*/

LISTE_PERSONNE ChargerListeParChamp()

{

      FILE *fic;

      char nomfic[20], car, s_age[20];

      LISTE_PERSONNE l, lp;

 

      lp = NULL;

      printf("\nNom du fichier : ");

      scanf("%s",&nomfic);

      fic = fopen(nomfic,"r");

      if (fic == NULL)

            printf("Le fichier %s n'a pas ete ouvert",nomfic);

      else

      {

            if (feof(fic))

                  car = EOF;

            else car = '\n';

            while (car != EOF)

            {

                  l = malloc(sizeof(CEL_LISTE_PERSONNE));

                  /* valeurs par défaut */

                  strcpy(l->nom,"");

                  strcpy(l->prenom,"");

                  l->age = 0;

                  car = getchamp(fic, l->nom, 20);

                  if (car != EOF && car != '\n')

                        car = getchamp(fic, l->prenom, 20);

            if (car != EOF && car != '\n')

                  {

                        car = getchamp(fic, s_age, 20);

                        if (s_age != "")

                             l->age = atoi(s_age);

                  }

 

                  if (car != EOF && car != '\n')

                        car = finirligne(fic);

 

                  if (strlen(l->nom) == 0)

                        free(l);

                  else

                  {

                        l->suivant = lp;

                        lp = l;

                  }

            }

            fclose(fic);

            lp = inverse(lp);

      }

      return lp;

}

 

Format csv non simplifié

 

S’il y a des « ; » dans un champ, on l’écrit entouré de guillemets.

ab;cd   s’écrit   "ab;cd"

S’il y a des « " » dans un champ, on l’écrit entouré de guillemets et on double les guillemets du champ.

ab"cd  s’écrit   "ab""cd"

 

Cela complique un peu les fonctions de lecture et d’écriture, mais permet de tout écrire et de tout relire.