IUT FV Bandjoun // Département d’Informatique (IG1 2008) LANGAGES DE PROGRAMMATION « Structures de Stockage : Les FICHIERS - LANGAGE C » 1. LA STRUCTURE DE « SUITE » Une structure de suite peut être déclarée comme suit: File *f ; Tous les éléments de la suite sont du même type (To) appelé type de base de la suite, To pouvant être de type simple (char, int, foat, ...) ou structuré (struct, typedef, ...) une suite de n éléments sera notée f = < fo, f1, f2, f3, ......, fn-1 > Cette suite ressemble à la structure de tableau à la différence que la dimension du tableau est fxée à la création alors que dans la structure de suite la dimension est ouverte et varie au cours de l’exécution du programme . la conséquence est qu’un schéma d’allocation dynamique doit être employé comme pour toutes les structures dont la cardinalité est « infnie ». On place la suite dans les structures fondamentales car le mécanisme d’allocation d’un emplacement mémoire (stockage) peut être effectué simplement, pour autant que l’on se restreigne à certaines contraintes lors de l’utilisation des suites. La contrainte se résume à l’usage restrictif des seuls accès séquentiels pour les suites. Cela veut dire que les suites seront utilisées en procédant d’un élément à son suivant immédiat et en construisant les suites par ajout répété d’un élément à son extrémité. Cela signife que les éléments ne sont pas directement accessibles à priori, ce qui constitue la différence fondamentale avec les tableaux. On utilisera le terme « fchier séquentiel » pour les suites, et en général, le terme séquentiel sera implicite. 2. OPERATEUR S SUR LES « SUITES » (Fichiers en Turbo C) La simplicité de l’accès séquentiel peut être renforcée par l’usage d’un ensemble exclusif d’opérateurs bien défnis, commun à tout les types de fchiers. ■ FILE = fopen(const char *flename, const char *mode); //… ouvre un fux. On associe un fchier réel constant (sur disquette par exemple) qui est le fchier physique (flename) à un nom variable dans le programme qui est le fchier logique (FILE). Mode est le mode d’accès au fchier ouvert (r, w, a, r+, w+, a+, etc.) - w défnit FILE comme la suite vide de longueur 0, ouvre - // … écrit n éléments ayant chacun une taille de size octets à la fn (suite) du fchier. ■ int feof(FILE *stream); //…teste l'arrivée en fn de fchier fux. feof est une macro qui scrute un fux à la recherche du marqueur de fn de fchier. ■ int fclose(FILE *stream); //ferme un fchier fux nommé stream Le concept de fchier joue un rôle essentiel et constitue une abstraction de données rangées sur un dispositif: • bandes, disques, lecteurs de cartes ... (lecture et écriture). • imprimantes (écriture). • clavier (lecture). ceci permettant d’exprimer d’une manière générale les caractéristiques et opérations qui leur sont communes. 3. FICHIERS D’ENTIERS (et générateur de nombres aléatoires) Il est de grande importance pour la simulation de phénomènes physiques de disposer d’un générateur de nombres aléatoires. Mais il n’existe pas de moyens de produire de vrais nombres aléatoires à partir d’un ordinateur ni d’ailleurs de n’importe quel système déterministe. Il est pourtant possible de construire des générateurs de nombres pseudo-aléatoires distribués de façon uniforme . C’est la propriété de la fonction random du TURBO C qui crée un nombre compris dans [0..1[, et random(n) qui crée un entier compris dans [0..n-1]. Les nombres pseudo-aléatoires distribués selon une loi non uniforme seront déterminés à partir de la distribution uniforme. Exercice 1: •Ecrire le programme qui crée un fchier de 1000 entiers uniformément répartis entre 0 et 20, dénommé Notes.data et placé dans le sous-répertoire IG •Ecrire le programme qui comptera le nombre d’apparitions de chaque Note (on rangera ces fréquences dans un tableau). On affchera le résultat et on dessinera ensuite l’histogramme des fréquences. Conclusion ? •Ecrire le programme qui déterminera pour chaque valeur la plus longue suite de valeurs identiques. le fchier en écriture. Exercice 2: a ouvre le fchier Void création (char* str1, str2); File *fc1, *fc2 ; int i ; { en lecture-écriture (mise à jour) et place la position courante au début de FILE. - r ouvre le fchier en lecture et place la position courante au début de FILE. ■ int creat(const char *path, int amode); // Crée un nouveau fchier ou vide son contenu si le fchier existe. Path désigne le nom du fchier sur le support extérieur (ex: lecteur de disquette). Sa présentation dépendra de « l’Operating System » ( ″″a:\ig1\monfchier.doc ″″pour le cas de MS DOS). Amode désigne le mode d’ouverture : S_IREAD | S_IWRITE ■ size_t fread(void *ptr, size_t size, size_t n, FILE *stream); // … lit un nombre d'éléments de données de taille identique depuis un fux en entrée vers un bloc mémoire. Les éléments sont placés à la position courante et le pointeur avance sur l’élément (position) suivant. ■ size_t fwrite(const void *ptr, size_t size, size_t n, FILE*stream); Dr. NKENLIFACK Marcellin fc1=fopen(str1,"w+"); fc2=fopen(str2,"w+"); for (i=1; i<=100; i++) { if (random ( 2)==0) fwrite(&i,sizeof(i),1,fc1); else fwrite(&i,sizeof(i),1,fc2); } fclose(fc1) ; fclose(fc2) ; } Soient les fchiers F.num et G.num qui contiennent des suites ordonnées d’entiers F1, F2, ..., Fm et G1, G2, ..., Gn tels que: Fi+1 ≤≤Fi et Gj+1 ≤≤Gj pour tout i, j . * Ecrire le programme commenté qui fusionne ces deux fchiers en un fchier ordonné H.num tel que Hk+1 ≤≤Hk pour k=1, 2, ..., m+n-1; Page 1 / 5 IUT FV Bandjoun // Département d’Informatique (IG1 2008) LANGAGES DE PROGRAMMATION « Structures de Stockage : Les FICHIERS - LANGAGE C » 4. FICHIERS DE CARACTERES Exercice 3: * Ecrire le programme qui lit un fchier de caractères, par exemple votre plus long programme, qui compte la fréquence d’apparition des lettres de l’alphabet et dessine l’histogramme des fréquences. * Ecrire le programme qui lit un fchier de caractères, par exemple votre plus long programme, qui restitue ce fchier à l’écran et l’imprime en même temps. Nota Bene: l’écran de votre micro-ordinateur est un fchier particulier considéré comme une « valeur » par défaut (stdout = fux sortie); il en est de même pour le clavier (stdin = fux des entrées par défaut). L’imprimante est un fchier particulier et la variable fchier qui désigne l’imprimante est prédéfnie: stdprn. On peut également utiliser la variable PRN prédéfnie en défnissant son propre fchier d’impression : #include <stdio.h> void main(void) { FILE *imprimante; char mot1[24]="test impression numero 1"; char mot2[24]="test impression numero 2"; imprimante = fopen ("prn", "w"); fprintf(imprimante, "%s\n",mot1); fprintf(imprimante, "%s\n",mot2); fprintf(imprimante,"\nFIN IMPRESSION: Merci ... "); } Exercice 4: L’éditeur du TURBO C constituerait un traitement de texte convenable, pour autant que l’on puisse imprimer à sa convenance des mots en gras, en souligné, ou les deux conjointement (la liste n’est pas exhaustive et l’on pourrait par exemple imprimer les indices et les exposants, ...). •Ecrire le programme qui lit un fchier de caractères, qui l’imprime (d’abord à l’écran, puis directement à l’imprimante) et qui en particulier commence l’écriture en gras à la lecture du caractère ‘&’ (mais qui ne l’imprime pas) et termine l’écriture en gras au prochain ‘&’ (sans l’imprimer). Idem pour le soulignage en utilisant le caractère ‘$’. Nota Bene: L’imprimante passe en caractère gras par la commande suivante: fprintf(stdprn,"%c%c%",27,69); et supprime le mode gras par la commande suivante: fprintf(stdprn,"%c%c%",27,70); L’imprimante passe en mode souligné par la commande suivante: fprintf(stdprn,"%c%c%d",27,45,1); et supprime le mode souligné par la commande suivante : fprintf(stdprn,"%c%c%d",27,45,0); Exercice 5: Comparer deux fchiers, l’un ″″*.C″″ l’autre ″″*.bak″″, on affchera chaque ligne l’une après l’autre, avec arrêt après la première différence qui sera signalée par la sonnette. 5. OPERATEURS PERMETTANT UN ACCES DIRECT SUR LES FICHIERS Le TURBO C défnit des opérateurs supplémentaires qui permettent un accès direct dans le cas de fchiers dont les éléments sont identiques. Ce sont : Dr. NKENLIFACK Marcellin • int fseek (FILE *stream, long offset, int whence); // fait pointer le pointeur de fchier associé au fux stream à la position située offset octets au-delà de l'emplacement indiqué. Whence permet de déterminer le point de départ de la recherche dans le fchier : SEEK_SET Recherche depuis le début du fchier, SEEK_CUR Recherche depuis la position courante, SEEK_END Recherche depuis la fn du fchier ■ void rewind(FILE *stream); //… Repositionne le pointeur de fchier sur le début d'un fux. rewind(stream) équivaut à fseek(stream, 0L, SEEK_SET) sauf que rewind remet à zéro les indicateurs de fn de fchier et d'erreur, alors que fseek ne le fait que pour celui de fn de fchier. ■ int fgetpos(FILE *stream, fpos_t *pos); //… donne la valeur courante du pointeur d'un fchier fux ■ int fsetpos(FILE *stream, const fpos_t *pos); //…modife la valeur courante du pointeur d'un fchier fux fgetpos sauvegarde la position du pointeur de fchier associé au fux stream dans l'emplacement *pos pointé par pos. La valeur exacte ne vous est d'aucune utilité ; fsetpos affecte une nouvelle valeur au pointeur de fchier associé au fux stream. Le nouvel emplacement est une valeur obtenue par un précédent appel à fgetpos sur ce fux. fsetpos efface l'indicateur de fn de fchier du fchier pointé par stream et annule les effets d'appels à ungetc sur ce fchier. ■ long ftell(FILE *stream); // renvoie la position courante d'un pointeur de fchier du fux stream Exercice 6: écrire une fonction qui renvoie la taille (nombre d’éléments de la suite) d’un fchier quelconque. long flesize(FILE *stream) { long curpos, length; curpos = ftell(stream); fseek(stream, 0L, SEEK_END); length = ftell(stream); fseek(stream, curpos, SEEK_SET); return length; } 6. MANIPULATION DES FICHIERS FICHIER D’ARTICLES (enregistrements) Le langage C permet l’accès aux fchier structurés (de n’importe quel type), aux fchiers binaires, … Il se confrme indéniablement comme étant un bon langage de « bas niveau », que ce soit en général ou en ce qui concerne les manipulations de fchiers. Nous commençons par manipuler les fchier structurés simplement. 6.1. CREATION DE FICHIERS STRUCTURES 6.1.1. exemple global /* FIALEA.C -- CREATION DE FICHIER STRUCTURE D’ELEMENTS */ #include <stdio.h> #include <stdlib.h> #include "fonchar.c" //le fichier "fonchar.c" contient le corps des fonctions gencaralea() et genstralea() #define N 10 Page 2 / 5 IUT FV Bandjoun // Département d’Informatique (IG1 2008) LANGAGES DE PROGRAMMATION « Structures de Stockage : Les FICHIERS - LANGAGE C » #define LP 15 #define LN 25 typedef struct { char nom[LN], prenom[LP]; int ds1,ds2,ds3, ds4; float moy; }Tetud, *Tptretud; int main() { FILE *f; int i,j; Tetud * ptretud; Tetud etud; clrscr(); ptretud=(Tptretud) calloc(1,sizeof(Tetud)); if (ptretud==NULL) { printf("erreur d'allocation memoire"); return(1); } f=fopen("e:\\sysnk\\tpetud\\tc30\\doc\\essai1. doc","wb+"); if (f== NULL) { printf("erreur de creation de fichier"); return(1); } for (i=0;i<N;++i) { genstralea(etud.nom,LN); genstralea(etud.prenom,LP); etud.ds1=rand()%21; etud.ds2=rand()%21; etud.ds3=rand()%21; etud.ds4=rand()%21; etud.moy=(float)(etud.ds1+etud.ds2+etud.ds3+et ud.ds4)/4; ptretud[0]=etud; // *ptretud=etud; printf("%-25s%15s%6d%6d%6d%6d%8.2f\n",(*ptretud).nom,(*ptretu d).prenom, ptretud->ds1,ptretud>ds2,ptretud[0].ds3,ptretud[0].ds4,ptretud[0].m oy); fwrite(ptretud, sizeof(Tetud), 1, f); } rewind(f); printf("\n..Lecture et affichage des elements du fichier...\n\n"); while (fread(ptretud, sizeof(Tetud), 1, f)) { printf("%-25s%15s%6d%6d%6d%6d%8.2f\n",(*ptretud).nom,(*ptretu d).prenom, ptretud->ds1,ptretud>ds2,ptretud[0].ds3,ptretud[0].ds4,ptretud[0].m oy); } fclose(f); getch(); return(0); } 6.1.2. 2ème exemple global /* FIALE2.C -- CREATION DE FICHIER STRUCTURE ET AFFICHAGE DES ELEMENTS LUS PAR BLOC */ #include <stdio.h> Dr. NKENLIFACK Marcellin #include <stdlib.h> #include "fonchar.c" //le fichier "fonchar.c" contient le corps des fonctions gencaralea() et genstralea() #define N 10 #define LP 15 #define LN 25 typedef struct { char nom[LN], prenom[LP]; int ds1,ds2,ds3, ds4; float moy; }Tetud, *Tptretud; int main() { FILE *f; int i,j; Tptretud ptretud1,ptretud5; Tetud etud; clrscr(); ptretud1=(Tptretud) calloc(1,sizeof(Tetud)); ptretud5=(Tptretud) calloc(5,sizeof(Tetud)); if (ptretud1==NULL) { printf("erreur d'allocation memoire"); return(1); } f=fopen("e:\\sysnk\\tpetud\\tc30\\doc\\essai1. doc","wb+"); if (f==NULL) { printf("erreur de creation de fichier"); return(1); } for (i=0;i<N;++i) { genstralea(etud.nom,LN); genstralea(etud.prenom,LP); etud.ds1=rand()%21; etud.ds2=rand()%21; etud.ds3=rand()%21; etud.ds4=rand()%21; etud.moy=(float)(etud.ds1+etud.ds2+etud.ds3+et ud.ds4)/4; *ptretud1=etud; /* ptretud1[0]=etud; */ printf("%-25s%15s%6d%6d%6d%6d%8.2f\n",(*ptretud1).nom,(*ptret ud1).prenom, ptretud1->ds1,ptretud1>ds2,ptretud1[0].ds3,ptretud1[0].ds4,ptretud1[0 ].moy); fwrite(ptretud1, sizeof(Tetud), 1, f); } rewind(f); printf("\n..Lecture par bloc de 5 et affichage des elements du fichier...\n\n"); while (fread(ptretud5, sizeof(Tetud), 5, f)) { /*..lecture du fichier par bloc de 5 elements..*/ for (i=0;i<5;++i) { printf("%-25s%15s%6d%6d%6d%6d%8.2f\n",ptretud5[i].nom,ptretud 5[i].prenom, ptretud5[i].ds1,ptretud5[i].ds2,ptretud5[i].ds 3,ptretud5[i].ds4,ptretud5[i].moy); Page 3 / 5 IUT FV Bandjoun // Département d’Informatique (IG1 2008) LANGAGES DE PROGRAMMATION « Structures de Stockage : Les FICHIERS - LANGAGE C » } } fclose(f); getch(); return(0); } Exercice 7: Ecrire un programme généralisé qui crée un fchier DUTIG de 150 articles de type : Tetudiant et contenant entre autres { nom : string[18] ; classe : string[4] ; note1 : 0..20 ; note2 : 0..20 ; moy : real ; reussi : boolean ; /* déf. True=0, False=1 */ classement : int ; } Le champ nom sera une chaîne de caractères construite de façon aléatoire et de longueur [5..18]. Le champ classe aura aléatoirement la valeur : IG1, IG2 , IG3. Les notes des valeurs aléatoires [0..20], la moyenne initialisée à -l et le champ reussi initialisé à False. Exercice 8 (suite exercices précédents): • • Ecrire le programme qui calcule la moyenne pour chaque étudiant, et met le champ réussi à True si la moyenne est ≥≥ 10. Ecrire le programme qui affche les étudiants de DUTIG ayant réussi. On vérifera que la moyenne a été calculée correctement. 7. TRI D’UN FICHIER /* FICETRI.C -- GESTION DE FICHIER STRUCTURE ET TRI SUR SES ELEMENTS */ #include <stdio.h> #include <sys\stat.h> // bibliotheque contenant des informations au sujet de fichier ou de r‚pertoire #include <stdlib.h> #include "fonchar.c" //le fichier "fonchar.c" contient le corps des fonctions gencaralea() et genstralea() #define LP 10 #define LN 20 typedef struct { char nom[LN], prenom[LP]; int ds1,ds2,ds3, ds4; float moy; }Tetud, *Tptretud; typedef struct { char nom[LN]; unsigned long cpos; }Tnoeud, *Tptrnoeud; void triselect(Tptrnoeud l, int taille) { int k,j,min; Tnoeud s; for(k=0;k<taille;++k) { min=k; Dr. NKENLIFACK Marcellin for(j=k+1;j<taille;++j) { if (strcmp(l[j].nom,l[min].nom)<0) min=j; } s=l[min]; l[min]=l[k]; l[k]=s; } } main() { FILE *f; int i; unsigned long N; Tptretud ptretud; Tetud etud; Tnoeud lnoeud; Tptrnoeud ptrtab; struct stat statrecord; clrscr(); ptretud=(Tptretud) calloc(1,sizeof(Tetud)); if (ptretud==NULL) { printf("erreur d'allocation memoire"); return(1); } if ((f=fopen("e:\\sysnk\\tpetud\\tc30\\doc\\essai3 .doc","wb+"))==NULL) //... utilisation du fichier cree par un aiutre programme <FICLISTL.C> { printf("erreur d'ouverture de fichier"); return(1); } //... utilisation du fichier cree par un autre programme:<FICLISTL.C> stat("e:\\sysnk\\tpetud\\tc30\\doc\\essai3.doc ",&statrecord); //... calcul du nombre d'elements du fichier a partir de la taille du fichier et de la taille d'element <Tetud> N=(statrecord.st_size)/sizeof(Tetud); printf("Nombre d'elements = %lu\n...\n", N); getch(); ptrtab = (Tptrnoeud) calloc sizeof(Tnoeud)); if (ptrtab==NULL) { printf("erreur d'allocation memoire"); return(1); } i=0; while (fread(ptretud, sizeof(Tetud), 1, f)) { printf("%-20s%10s%6d%6d%6d%6d%8.2f",ptretud->nom,ptretud>prenom, ptretud->ds1,ptretud->ds2,ptretud>ds3,ptretud->ds4,ptretud->moy); strcpy(ptrtab[i].nom, ptretud->nom); ptrtab[i].cpos=i; ++i; } printf("\n...FICHIER INITIAL...\n"); for (i=0;i<N;i++) printf("%20s%10lu\n",ptrtab[i].nom,ptrtab[i].cpos); triselect(ptrtab, N); // TRI du tableau... printf("\n...FICHIER TRIE ...\n"); for (i=0;i<N;i++) Page 4 / 5 (N, IUT FV Bandjoun // Département d’Informatique (IG1 2008) LANGAGES DE PROGRAMMATION « Structures de Stockage : Les FICHIERS - LANGAGE C » printf("%20s%10lu\n",ptrtab[i].nom,ptrtab[i].cpos); printf("\n..BIEN SUCCES NON!!!"); getch(); return(0); } { c = getc(fc1) ; c = c - alea ; putc(c, fc2) ; } fclose(fc1) ; fclose(fc2) ; } Exercice 9 (suite exercices précédents): Ecrire le programme qui effectue le classement des étudiants de DUTIG crées précédemment. Exercice 10 (suite exercice précédents): Ecrire le programme qui crée un fchier trié alphabétiquement pour chaque classe (TRI par nom, prenom et classe), en utilisant le fchier précédent. 8. CRYPTAGE DES FICHIERS Le principe du cryptage d’un fchier « fc1 » pour obtenir un fchier « fc2 » se résume en l’utilisation d’une fonction bijective « C » qu’on applique sur fc1. C(fc1) = fc2 L’opération inverse (décryptage) consiste à effectuer C-1 (fc2) = fc1. La fonction f(x) = x+a est bijective et f -1 (x) = x-a. Problème : On suppose que vous disposez d’une fonction génératrice de nombres pseudo-aléatoire intitulée int alea ( ) ; qui génère une suite pseudo-aléatoire de nombres compris dans l’intervalle [0.. n] on utilisera cette fonction pour coder les caractères d’un fchier texte (ex. un de vos programmes C) afn de rendre celui-ci incompréhensible. On demande d’écrire 2 procédures : Void Lisible ( char* ch1, ch2 ) ; créant à partir d’un fchier de nom physique ch1, un fchier crypté de nom physique ch2. Void ILisible ( char* ch1, ch2 ) ; créant à partir d’un fchier crypté de nom physique ch1, un fchier décrypté de nom physique ch2. On écrira un programme pour tester ces procédures Void Lisible ( char* ch1, ch2 ) ; { File *fc1, *fc2 ; Char c ; fc1 = fopen(ch1,"r") while ( !feof(fc1)) { c = getc(fc1) ; c = c + alea ; putc(c, fc2) ; } fclose(fc1) ; fclose(fc2) ; } Void ILisible ( char* ch1, ch2 ) ; { File *fc1, *fc2 ; Char c ; fc1 = fopen(ch1,"r") while ( !feof(fc1)) Dr. NKENLIFACK Marcellin Page 5 / 5