Les Fichiers
Les fichiers standards Ceux qu’on a utilisés jusqu’à présent sont le clavier et l’écran: ce sont des fichiers standards. Les instructions du langage C appropriées pour les utiliser sont comme suit:
Les entrée-sorties Après avoir exploré les types de données et les opérateurs du langage C nous passons maintenant à l'étude de quelques fonctions qui vont nous permettre de lire à partir du clavier et d'afficher sur l'écran de notre ordinateur. Après tout, tout programme aura besoin de données que nous devons lui fournir pour les manipuler et affichera en retour différents résultats sur l'écran.
Le langage C nous fournit des fonctions prédéfinies de lecture à partir du clavier et d'écriture sur l'écran qui sont contenues dans le "header file" stdio.h donc au début de tout programme qui devra utiliser ces fonctions nous devrons inclure au début la directive suivante: #include <stdio.h>
La fonction getchar(); La fonction getchar permet la récupération d'un seul caractère à partir du clavier. La syntaxe d'utilisation de getchar est la suivante: variable=getchar(); notez que variable doit être de type char et les parenthèses aprés getchar.
La fonction putchar(); La fonction putchar permet l'affichage d'un seul caractère sur l'écran de l'ordinateur. La fonction putchar constitue alors la fonction complémentaire de getchar. La syntaxe d'utilisation est la suivante: putchar(variable); où variable est de type char. Exemple: #include main() { char c; c=getchar(); putchar(c); }
La fonction scanf(); Les deux fonctions déjà évoquées bien qu'utiles présentent un inconvénient majeur: que faire pour pouvoir entrer une chaîne complète ou même un nombre? Dans le cas d'un chaîne on devra construire une boucle et dans le cas d'un nombre on devra aussi convertir les caractères en chiffres, tout celà se montre très lourd. c'est alors que vient à l'aide la fonction scanf. En effet elle permet de récupérer n'importe quelle combinaison de valeurs numériques, de caractères et de chaînes. La syntaxe d'utilisation de scanf est la suivante:
scanf(chaîne de contrôle, var1, var2, ..., varN); "où chaîne de contrôle" représente certaines données de formattage et "var1, var2,..., varN" représentent les variables à remplir. La chaîne de contrôle est entourée de guillemets constituée de groupes de caractères chaque groupe étant en relation avec une des variables mentionnées. Le groupe devra commencer par le signe % suivi d'un nombre indiquant la largeur de la variable à lire et ensuite un caractère bien défini (le type) indiquant si la variable est un caractère, un nombre (soit entier soit décimal) ou une chaîne.
La syntaxe du groupe possède alors la forme générique suivante: %[largeur]type Notez que l'argument entre crochets est facultatif. Les différents types sont donnés dans le tableau suivant. Si la chaîne contient plus qu'un groupe il sont souvent séparés par des espaces et ainsi les données entrées doivent être séparées par des espaces. Le type de chaque variable doit correspondre au type de son groupe et chaque variable doit être précédée du signe & sauf si elle est une chaîne. Théorie difficile? passons aux exemples:
Type de Données à récupérer c caractère d nombre entier e ou f un nombre décimal en notation réelle (f) ou exponentielle (e) s une chaîne de caractères
i est un entier. Pour obtenir i de l'utilisateur on écrit: scanf("%d",&i); sur le clavier nous tapons: -->> 123 2) a est un caractère: scanf("%c",&a); -->> d 3) ville est une chaîne: scanf("%s",ville); ^ |------------ pas de & car ville est une chaîne. -->> beyrouth
4) i et j sont deux entiers 4) i et j sont deux entiers. i ne doit pas être plus large que trois chiffres: scanf("%3d %d",&i,&j); -->> 231 1024 ^ |---------- notez l'espace separateur
La fonction printf(); Comme la fonction scanf(); permet d'acquérir tout type de données à partir du clavier, la fonction printf(); permet quant à elle d'afficher tout type de données à l'écran de l'ordinateur. Cette fonction permet d'afficher n'importe quelle combinaison de caractères de nombres et de chaînes. La syntaxe de la fonction printf(); est la suivante: printf("chaîne" de contrôle,arg1, arg2,..., argN);
où "chaîne de contrôle" représente certaines données de formattage et "arg1, arg2,..., argN" les variables (ou éventuellement les expressions) à afficher. chaque groupe est en relation avec l'argument correspondant dans la liste des arguments.
Le groupe possède la forme générique suivante: %[flag][largeur].[précision]type Les éléments entre crochets sont facultatifs. La largeur indique le nombre minimum de caractères affichés et la précision indique le nombre maximum de caractères affichés (pour un nombre décimal la précision indique le nombre maximal de caractères après la virgule qui est par défaut 6)
Les différents types sont donnés dans le tableau suivant: Type Signification c un caractère d un entier f un réel s une chaîne de caractères e notation exponentielle
Pour les entiers, nous avons en réalité d, u, o, x pour les entiers à afficher en (d)écimal , sans signe -- (u)nsigned, en (o)ctal sans signe, ou en he(x)adécimal sans signe. Ainsi -1 (la valeur maximum 11...1 sur 32 bits) donnera -1 en format d, 4294967295 (232) en format u, ffffffff en format x et 37777777777 en format o.
Pour les réels, nous avons: e, f, g pour les réels à afficher en format décimal flottant avec (e)xposant [+-]ddd.nnnnnnE[+-]xx, ou en format décimal format (f)ixe [-]ddd.nnnnnn. La longueur des n est donnée par la précision sinon elle est 6 par défaut. Le format g est pour l' affichage le plus précis dans le minimum d'espace.
float x; double y; x = y = acos((double) -1); printf("Simples flottants %f %e %g\n", x, x, x); printf("Flottants doubles %f %e %g\n", y, y, y); printf("Simples flottants+ %18.12f %18.12e %18.12g\n", x, x, x); printf("Flottants doubles+ %18.12f %18.12e %18.12g\n", y, y, y);
donne: Simples flottants 3.141593 3.141593e+00 3.14159 Flottants doubles 3.141593 3.141593e+00 3.14159 Simples flottants+ 3.141592741013 3.141592741013e+00 3.14159274101 Flottants doubles+ 3.141592653590 3.141592653590e+00 3.14159265359
Les différents "flags" sont donnés dans le tableau suivant: Flag Signification valeur par défaut - justification à gauche justification à droite + affichage du signe (+ ou -) uniquement le (-) est avant la valeur numérique affichée espace les valeurs positives sont aucun espace n’est précédées d’un espace affiché
Les fichiers non standards En réalité, le clavier et l’écran sont des fichiers qui ont le nom stdin et stdout, respectivement. En C, nous avons la possibilité de définir nos propres fichiers ans lesquels nous pouvons lire et écrire nos informations. Ces fichiers, à l’opposé du clavier et de l’écran, puevent être sauvegardés et consultés ultérieurement.
Pour utiliser un fichier, il est necessaire de le déclarer avant tout. Pour ce faire, on utliise le mot reservé FILE *non du fichier Exemple: FILE *infile; FILE *outfile;
Ceci a pour effet de créer logiquement un fichier Ceci a pour effet de créer logiquement un fichier. Cela n’est pas suffisant. Il faut encore lui donner une existence physique. Pour ce faire, on utilise: infile = fopen(fichier.txt, “r”); outfile = fopen(sortie.txt, “w”);
Si la fonction fopen n’est pas capable d’effectuer l’opération demandée, le pointeur de fichier qu’elle retourne est égal à la valeur associée avec l’identificateur NULL Si par exemple le fichier fichier.txt n’existe pas alors l’exécution de l’instruction suivante va afficher le message approprié suivant: if (infile == NULL) printf(“ne peut pas ouvrir en lecture le fichier fichier.txt\n”);
Une notion importante liée aux fichiers est celle de format Une notion importante liée aux fichiers est celle de format. On doit écrire l’information de façon à pouvoir la retrouver facilement. Il existe deux formats spécifiques en C : binaire et texte. Les fichiers en format texte regroupent en octets l’information et permettent d’écrire des tabulations qui seront reconnues de même que des sauts de lignes. Un entier écrit dans un fichier texte prendra en octets le nombre de chiffres de cet entier. Les fichiers binaires écrivent l’information comme une suite de bits. Un entier écrit dans ce format prendra toujours quatre octets d’espace.
Un fichier binaire est un fichier crée par un programme en exécution en sauvegardant directement dans ce fichier dans la représnattion binaire chaque élément de ce fichier.
Exemple #include <stdio.h> int main () { int y=123; FILE* Fichier = fopen ("File.txt", "w"); fwrite (&y, sizeof (int), 1, Fichier); fclose (Fichier); Fichier = fopen ("File.bin", "wb"); fprintf (Fichier, "%i", y); return 0; }
Le format de fichier va encore plus loin cependant Le format de fichier va encore plus loin cependant. Par exemple, les données peuvent être encryptées ou encore être mises en lignes ou en colonnes, etc. Type d’accès à un fichier Il existe deux types d’accès au fichier : direct et séquentiel. Un accès direct implique que l’on peut accéder aux parties d’informations importantes d’un fichier sans avoir à lire les autres. Dans un accès séquentiel, il faut lire toutes les données précédentes pour arriver à ce qui nous intéresse.
Prenons un exemple. Supposons que l’on ait les deux fichiers suivants : directe.txt PatriceMarcMilesDavisCliffBurtonDonna sequentiel.txt
On voit que puisque la taille des enregistrements de Directe On voit que puisque la taille des enregistrements de Directe.txt est de 20 caractères il est possible de calculer où commence un enregistrement. Dans le cas de sequentiel.txt, c’est impossible, il faut lire le fichier pour savoir où sont les espaces. Il est possible d’effectuer un accès directe sur un fichier avec un fichier binaire et avec un fichier texte. Cependant, la plupart du temps, lorsque l’on veut effectuer un accès directe sur un fichier on utilise les fichiers binaires. .
"r" : en lecture seulement mode texte. Principales fonctions de gestion de fichiers. Ouverture/fermeture FILE* fopen (const char* filename, const char* mode); Ouvre le fichier « filename » selon le mode mode indiqué. Retourne un pointeur sur le fichier. Le pointeur NULL est retourné si l’opération d’ouverture échoue. "r" : en lecture seulement mode texte. "w" en écriture seulement et mode texte. "wb" : en écriture seulement mode binaire. "rb" : en lecture seulement mode binaire.
Lecture et écriture int fread (void* buffer, size_t size, size_t count, FILE* stream); Lit un bloc de données d’un flux. Précisément, lit count blocs de taille size du flux stream et enregistre cette information dans buffer. Notons que la lecture est sans format. Retourne le nombre de blocs lus. Si la valeur retournée est différentes de count, une erreur est survenue ou la fin du fichier.
int fscanf ( FILE * stream , const char * format [ , argument , ...] ); Lit des données formatées du flux stream. La syntaxe est la même que celle de scanf. Exemple : scanf (Fichier, "%s", Nom) ; scanf (Fichier, "%i", &x) ; size_t fwrite ( const void * buffer, size_t size, size_t count, FILE * stream ); Écrit un bloc de données dans un flux. Le bloc sera composé de count blocs de taille size. Le flux est stream et l’information est lue dans buffer. int fprintf (FILE * stream , const char * format [ , argument , ...] ); Écrit un bloc de données formatées. Même format que printf. Exemple : printf ("Salut %s, tu as %i ! ! ", Nom, Age) ; printf ("%i", Taille) ;
Position dans un fichier long ftell ( FILE * stream ); Retourne la position du pointeur stream. Lorsque le fichier est en mode binaire, le nombre retourné est le nombre d’octets du début à la position courante. En mode texte, à cause des retours de chariots, cette fonction est moins utilisée. Retourne le pointeur stream si tout s’est bien déroulé. Sinon, la valeur –1 est retournée. int fseek ( FILE * stream , long offset , int origin ); Repositionne le pointeur stream. La nouvelle position est un déplacement de taille offset par rapport à origin. Les valeurs possibles pour origin sont : SEEK_SET (0) : Début du fichier SEEK_CUR (1) : Position courante SEEK_END (2) : Fin du fichier.
Un exemple Écrire un programme qui copie un fichier dans un autre fichier.
#include <stdio.h> const int MAX = 20; int mai () char in_name[MAX] ; char out_name[MAX]; FILE *inp, *outp; char car; printf(“introduire le nom du fichier à copier”); for (scanf(“%s”, in_name); (inp =fopen(in_name,”r”))==NULL; scanf(“s%”, in_name)) { printf(“ne peut pas ouvrir %s pour lecture\n”,in_name); printf(“ré-introduire le nom de fichier”); }
/* avoir le nom du fichier dans lequel on veut écrire*/ printf(“introduire le nom du nouveau fichier”); outp =fopen(out_name, “w”) for (car = getc(inp); car != eof; car =getc(inp)) putc(car, outp); fclose(inp); fclose(outp); printf(“copié %s à %s\n”, in_name, outp); return(0); }
Exemple 2 Créer un fichier qui contient tous les nombres binaires entre 2 et 500.
#include <stdio.h> FILE *binaryp void main() { int i; binaryp = fopen(“nums.bin”, “wb”); for (i=2; i <= 500; i+=2) fwrite(&i, sizeof(int), 1, binaryp); fclose(binaryp); }
Remarquer que dans ce cas, pour écrire dans le mode binaire nous avons besoin de l’instrcution fwrite: fwrite(&i, size(int), 1, binaryp) Le premier paramètre indique l’adresse de la variable à écrire Le deuxième paramètre indique la longueur de chaque élément à écrire Le troisième paramètre indique le nombre de valeur à écrire Enfin le quatrième paramètre indique le ponteur de fichier sur lequel va porter cette écriture.
Exemple d’utilisation des fonctions de base sur les fichiers. #include <stdlib.h> #include <stdio.h> const int MAX = 20; struct Eleve { char Nom[MAX]; char Prenom[MAX]; int Age; };
void DemanderNomFichier (char NomFichier[MAX]); bool FichierExiste (char Nom[MAX]); void Remplir (Eleve* Pat); void Afficher (Eleve* Pat); void LireInfo (char NomFichier[MAX], Eleve* E); void GarderInfo (char NomFichier[MAX], Eleve* E); void AfficherFichier (char NomFichier[MAX]); void AfficherFichierOk (char NomFichier[MAX]); void AfficherEnr (char NomFichier[MAX], int NoEnr);
int main () { char NomFichier[MAX]; DemanderNomFichier (NomFichier); Eleve* Pat = (Eleve*) malloc(sizeof (Eleve)); Remplir (Pat); Afficher (Pat); GarderInfo (NomFichier, Pat); Eleve *Bob = (Eleve*) malloc(sizeof (Eleve));
LireInfo (NomFichier, Bob); Afficher (Bob); printf ("\n\n\n"); AfficherFichier (NomFichier); AfficherFichierOk (NomFichier); int NoEnr=0; printf ("Numero d'entregistrement?"); scanf ("%i", &NoEnr); AfficherEnr (NomFichier, NoEnr); return 0; }
void DemanderNomFichier (char NomFichier[MAX]) { bool Existe=true; do printf ("Quel est le nom du fichier?"); scanf ("%s", NomFichier); if (Existe = FichierExiste (NomFichier)) printf ("Existe\n"); else printf("Non, existe pas\n"); } while (!Existe);
bool FichierExiste (char Nom[MAX]) { bool Retour = true; FILE* Fichier = fopen (Nom, "rb"); if (Fichier != NULL) fclose (Fichier); Retour = true; } else Retour = false; return Retour;
void GarderInfo (char NomFichier[MAX], Eleve* E) { FILE* Fichier = fopen (NomFichier, "ab"); fwrite (E, sizeof (Eleve), 1, Fichier); fclose (Fichier); }
void LireInfo (char NomFichier[MAX], Eleve* E) { FILE* Fichier = fopen (NomFichier, "rb"); fread (E, sizeof (Eleve), 1, Fichier); fclose (Fichier); }
void AfficherFichier (char NomFichier[MAX]) { printf ("Le contenu de %s est : \n", NomFichier); FILE* Fichier = fopen (NomFichier, "rb"); int No=0; while (!feof (Fichier)) No++; Eleve E; fread (&E, sizeof (Eleve), 1, Fichier); printf ("Numero %i : \n",No); Afficher (&E); } fclose (Fichier);
void AfficherFichierOk (char NomFichier[MAX]) { printf ("\n\n\nLe contenu de %s est : \n", NomFichier); FILE* Fichier = fopen (NomFichier, "rb"); fseek (Fichier, 0, SEEK_END); int Nombre = ftell(Fichier)/sizeof (Eleve); fseek (Fichier,0, SEEK_SET); int No=0; for (int y=0; y <Nombre; y++) No++; Eleve E; fread (&E, sizeof (Eleve), 1, Fichier); printf ("Numero %i : \n",No); Afficher (&E); } fclose (Fichier);
void AfficherEnr (char NomFichier[MAX], int NoEnr) { FILE* Fichier = fopen (NomFichier, "rb"); fseek (Fichier, (NoEnr-1)*sizeof(Eleve), SEEK_SET); Eleve E; fread (&E, sizeof (Eleve), 1, Fichier); fclose (Fichier); Afficher (&E); }