Structures
Annuaire Exercice 3
Enregistrement persistant
Accueil

du site

Ce que nous allons ajouter au programme "Annuaire"

Notre programme version 2 reste encore bien peu performant :

  • le fichier "Fichier" disparaît dès que l'on quitte le programme.

  • il occupe toujours en mémoire vive (RAM - DRAM) l'équivalent de 100 fiches, que l'on en ait saisi 0 ou 100 !
    De l'espace mémoire est inutilement perdu s'il y a peu de fiches.
    Le client sera limité à NB_MAX_FICHES, à moins de recompiler le programme,ce qu'il ne peut pas faire.

Le but des l'actuelle version 3 est de rendre le fichier persistant en l'enregistrant sur le disque dur ou toute autre mémoire persistante de votre ordinateur.

Les versions ultérieures du programme "Annuaire" résoudront, entre autre, le deuxième point signalé : la rigidité du stockage en mémoire.

Mais nous n'en sommes pas encore là !

Dans la suite, nous signalerons en particulier toutes les modifications aux versions précédentes des fichiers (surtout des ajouts)
Un lien aux fichiers définitifs sera donné pour chacun.

Cours
Fichier.cpp
Annuaire.cpp
Résultat

 

Rappels : stockage persistant


Lors du déroulement du programme, les données doivent obligatoirement se trouver dans la mémoire vive (RAM - DRAM) car le processeur ne peut pas accéder directement au disque et pour des raisons de rapidité d'accès.

Mais ce type de mémoire perd toute information lors de la coupure de l'alimentation électrique (mémoire dite "volatile") ou lorsque l'on quitte le programme. Le segment de mémoire peut être uilisé par par d'autres programmes.

Si on veut conserver ces données, il faut les enregistrer dans une mémoire persistante : disque dur, disquette, CD-ROM, enregistreurs-lecteurs à bandes etc.

Il existe de très nombreuses procédures permettant des échanges de données entre la RAM et les supports de stockange persistant.

Nous allons utiliser les suivantes :

Fonctions
Définitions
// Manipulateur de fichier : ce n'est pas une fonction maisun obejt de type FILE.
Type FILE : FILE * stream;
// Fonction d'ouverture d'un fichier
FILE *fopen( const char *filename, const char *mode );
// Fonction d'écriture d'un fichier : données de la RAM vers le disque
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
// Fonction de lecture d'un fichier : données du disque vers la RAM
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
// Fonction de fermeture (clôture) d'un fichier
int fclose( FILE *stream );

Les prototypes de ces fonctions se trouvent dans <stdio.h>

Type "FILE" ( manipulateur de fichier : File Handler )


Au sens informatique, un fichier c'est l'ensemble :

  • d'un nom de fichier (comprenant ou non une extension)
    ex. : MonFichier.doc : "MonFichier" est le nom "doc" est l'extension.
  • d'une suite de secteurs magnétiques sur le disque ou la bande contenant les données
    La correspondance données-secteurs est (heureusement) entièrement gérée par le système d'exploitation (DOS signifie Disk Operating System) et transparente pour nous.
  • d'un espace mémoire vive (RAM) reflet exact des données contenues ou à contenir du disque..

Un "manipulateur de fichier" (file handler) est une valeur retournée par le système d'exploitation quand nous lui proposons d'ouvrir un fichier à l'aide de la procédure fopen(...).

Pour nous, humains, la meilleure manière de désigner un fichier c'est son nom de fichier.
Pour le système d'exploitation de l'ordinateur c'est plutôt ce "manipulateur de fichier".

Il est possible que dans l'opération d'ouverture du fichier (fonction fopen(...)) le système d'exploitation de l'ordinateur manque de ressources pour gérer le fichier que nous lui demandons d'ouvrir : plus assez de mémoire, dique plein, trop de fichiers ouverts etc.
Dans ce cas, il nous délivre une valeur particulière pour ce manipulateur : parfois la valeur NULL parfois (-1) tout dépend de la fonction d'ouverture utilisée - il n'y a pas que "fopen(...)".

Un manipulateur de fichier est du type "FILE" (ce type est défini dans les bibliothèques de programmes qui accompagnent le logiciel de développement ;son prototype se trouve dans <stdio.h>.

Ainsi, dans notre programme, nous devrons déclarer ainsi une variable manipulateur de fichier :

#include <stdio.h>
//......
FILE * pAF;
//pAF est ainsi déclaré comme un pointeur sur une variable de type FILE
//FILE est le type "manipulateur de fichier".
//......

 

Notes (questions soulevées par les étudiants)

Peu importe le nom que vous donnez au manipulateur de fichier.
On pourrait l'appeler "pPoeleAFrire" ou "RaquetteDeTennis" :

FILE *pPoeleAFrire, *RaquetteDeTennis;

Même le "p" de "pAF" n'est pas obligatoire pour en faire un pointeur.

Ca va sans dire, certes, mais l'expérience a montré que çà allait mieux en le disant !

Nous avons déclaré non pas une variable de type FILE mais un pointeur sur une variable de type FILE.
Pourquoi ?
Parceque la procédure "open(...)" nous retourne un pointeur sur le manipulateur de fichier et pas ce manipulateur.
Et aussi parceque les procédures fread(...) - fwrite(...) - fclose(...) utilisent un pointeur et pas la variable elle-même.

 


Voir ci-dessous dans la description de la procédure fopen() comment on obtient la valeur
du manipulateur du fichier que l'on veut ouvrir.

 

Procédure d'ouverture de fichier "fopen(...)"

FILE *fopen( const char *filename, const char *mode );

Paramètres :

  • filename : nom de fichier avec ou sans extension
    exemple : "MonFichier.apm"
  • mode : on peut ouvir un fichier dans plusieurs intentions : pour y lire des données, pour y écrire des données, pour lire et écrire à la fois etc.
    Ce champ peut prendre les valeurs :"r", "w", "a", "r+", "w+", "a+" décrites dans le tableau ci-dessous.

"mode"
Ouverture du fichier pour
"r"
lire (read) (faire passer des données du disque vers la mémoire)
"w"
écrire (write) (faire passer des données de la mémoire vers le disque)
"a"
ajouter des données dans le disque à la suite de celles qui s'y trouvent déjà : appending
"r+"
lire & écrire (le fichier doit préalablement exister pour pouvoir y écrire)
"w+"
lire & écrire (si le fichier existe il est écrasé par les nouvelles donnés, sinon il est créé).
"a+"
ajouter & lire (si le fichier n'existe pas il est créé)

Valeur retournée par fopen(... ) :

  • pointeur sur le manipulateur de fichier -file handler-
  • si le fichier ne peut pas être ouvert le système d'exploitation retourne la valeur NULL

Exemple :

#include <stdio.h>
//......
FILE * pAF;
//pAF est un pointeur sur un une variable de type FILE.
//......
pAF = fopen("FichierAnnuaire.fic","w+");
if(pAF == NULL)
{
 printf("Plus assez de ressources système : fermez des programmes inutiles\n");
 printf("Nous sommes désolés : nous arrêtons ce programme");

 exit(0);
}
else
{
 // Suite du programme ...
 //......

 

 

 

Procédure de lecture de fichier "fread(...)"

size_t fread( void *buffer, size_t size, size_t count, FILE *stream );

Note : size_t est un type particulier du compilateur utilisé pratiquement équivalent à un "unsigned"

Paramètres :

  • buffer : pointeur sur le début de l'espace mémoire devant recevoir les données lues
  • size : taille commune en octets de chacune des structures de donées à lire (exemple : des fiches)
  • count : nombre de structures à lire.
  • *stream : pointeur sur la variable représentant la valeur du manipulateur de fichier retourné par fopen( )

Valeur retournée par fread(... ) :

  • C'est le nombre de structures lues.
    Il peut être inférieur au nombre demandé par le paramètre "count"

#include <stdio.h>

//......
FILE * pAF;
unsigned nbFichesEchangees

Fiche Fichier[NB_MAX_FICHES];
//......

pAF = fopen("FichierAnnuaire.fic","w+");

if(pAF == NULL)
{
 printf("Plus assez de ressources système :");
 printf("Libérez de la mémoire en fermant des programmes\n");
 printf("Nous sommes désolés : nous arrêtons ce programme");

 exit(0);
}
else
{
 // Tentative de lecture de 37 fiches :
 nbFichesEchangees = fread(Fichier, sizeof(Fiche) ,37, pAF);
 

 // Suite du programme ...
 //......

 

Procédure d'enregistrement de fichier "fwrite(...)"

size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );

Note : size_t est un type particulier du compilateur utilisé pratiquement équivalent à un "unsigned"

Paramètres :

  • buffer : pointeur sur le début de l'espace mémoire contenant les données à enregistrer.
  • size : taille commune en octets de chacune des structures de donées à enregistrer (exemple : des fiches)
  • count : nombre de structures àenregistrer.
  • *stream : pointeur sur la variable représentant la valeur de le manipulateur de fichier retourné par fopen( )

Valeur retournée par fwrite(... ) :

  • C'est le nombre de structures réellement enregistrées.
    Il peut être inférieur au nombre demandé par le paramètre "count"


#include
<stdio.h>
//......

FILE * pAF;

Fiche Fichier[NB_MAX_FICHES];

unsigned nbFichesEchangees;
//......

pAF = fopen("FichierAnnuaire.fic","w+");
if(pAF == NULL)
{
 printf("Plus assez de ressources système.\n");
 printf("Fermez des programmes actifs\n");
 printf("Désolés, nous arrêtons ce programme");

 exit(0);
}
else
{
  // Tentative d'écriture de 37 fiches :
  nbFichesEchangees = fwrite(Fichier, sizeof(Fiche) ,37, pAF);
 

 // Suite du programme ...
 //......

 

Procédure de clôture d'un fichier "fclose(...)"

int fclose( FILE *stream );

Lors de l'appel à a procédure fopen(...) le système d'exploitation a alloué des ressources en vue de pouvoir réaliser les opérations ultérieurement demandées.
On a tout intérêt à libérer ces ressources en fin d'opérations sur le fichier.
Et c'est précisément ce que fait fclose(...)
N'oubliez pas, dans vos programmes, de clore tout ficher ouvert !

Paramètre :

  • *stream : pointeur sur la variable représentant la valeur du manipulateur de fichier retourné par fopen( )

Valeur retournée par fclose(... ) :

  • Zéro si le fichier a pu être clos normalement

 

Modifications du fichier "Fichier.cpp" du projet Annuaire
Dans ce qui suit, nous avons écrit des procéduresqui utilisent les fonctions brutes fopen, fread, fwrite, fclose
en les aménageant pour notre fichier particulier : Fiche Fichier[NB_MAX_FICHES]
C'est un travail de méthodologie, mais il permet aussi d'en faire comprendre l'usage plus progressivement

// .....
// ....
// A écrire à la suite de "Fichier.cpp" de la version précédente

//=======================================================
//Procédures relatives au stockage persistant des données
//=======================================================

// Procédure pour savoir si le fichier existe
// par exemple pour ne pas écraser le fichier précédent

bool FichierExiste(char * pszNomFichier)
{
 FILE * pAF;
 //Pointeur du manipulateur de fichier
 //Ttype FILE dans stdio.h)

 //Ouverture du fichier pour y lire (r = read)
 if( ( pAF = fopen( pszNomFichier, "r" ) ) == NULL )
  return false;

 fclose(pAF);
 return true;
 }
}

//-------------------------------------------------------------------

//Procédure d'écriture dans "Fichier", notre tableau de "Fiche"
bool EcritFichier(char * pszNomFichier,Fiche *pFichier,unsigned nbFiches)
{
 FILE * pAF;


 
//Ouverture du fichier pour y écrire (w = write)
 if ((pAF = fopen( pszNomFichier, "w" )) == NULL )
  return false;


 //Ecriture
 fwrite(pFichier, sizeof(Fiche) , nbFiches, pAF);
 fclose(pAF);

 return true;
}
//-------------------------------------------------------------------

//Procédure de lecture dans "Fichier", notre tableau de "Fiche"
bool LitFichier(char * pszNomFichier,Fiche *pFichier, unsigned nbFiches)
{
 FILE * pF;

 //Ouverture du fichier pour y lire (r = read)
 if ((pAF = fopen( pszNomFichier, "r" )) == NULL )
  return false;

 fread(pFichier, sizeof(Fiche) ,nbFiches, pAF);
 fclose(pAF);
 return true;

}

 

Modifications du fichier "Annuaire.cpp" du projet Annuaire

//*************************************************************
//Programme "ANNUAIRE"
//FICHIER Annuaire.cpp (Version 3)
//Programme principal
//*************************************************************

#include <stdio.h>
#include "Fiche.h"

extern Fiche Fichier[NB_MAX_FICHES];

int main()
{
 char entree;

 
 if(FichierExiste("FichierAnnuaire.apm"))
   LitFichier("FichierAnnuaire.apm",Fichier,NB_MAX_FICHES);
  else
   RazFichier(Fichier);


 do
 {
  printf("\n\n Annuaire \n");
  printf("=========================\n");
  printf("Choix a votre disposition\n");
  printf("Saisir une fiche : S\n");
  printf("Visualiser le fichier : V\n");
  printf("Quitter : Q\n");
  printf("Entrer votre choix : ");
  scanf("%c",&entree);
  rewind(stdin);

  switch(entree)
  {
   case 'S' :
   case 's' : SaisieFichier(Fichier); break;

   case 'V' :
   case 'v' : VisuFichier(Fichier);break;

   case 'Q' :
   case 'q' : printf("\nFin de Saisie & Visualisation.\n");
   break;

   default : printf("\nEntrez S, V ou Q !\n");
   break;

  }
 }while((entree != 'Q')&&(entree != 'q'));

 //Enregistrons maintenant les changements :
 EcritFichier("FichierAnnuaire.apm",Fichier,NB_MAX_FICHES);

 printf("Au revoir et merci.\n");
 getchar();
 return 0;
}

 

Les résultats

1 - Je lance le programme et je saisis nos anciens copains jadis perdus :

2 - Je quitte ce programme, j'éteins mon ordinateur, je pars en vacances.

3 Je reviens de vacances,



4 J'allume mon ordinateur, je lance mon programme et, sans rien saisir, je demande à visualiser les fiches :

Toto et Titine sont bien là : on-a-ga-gné !


Si je regarde maintenant, à l'aide de l'explorateur de Windows
le répertoire sous lequel je développe ces modestes programmes qui vous sont destinés,
je vois apparaître le fichier "FichierAnnuaire.apm" créé par notre programme.

Ne ratez pas la suite : elle est mémorable et dynamique !


Retour à Annuaire 2
La suite d'Annuaire 3
Sommaire Langage C
Accueil

du site