XI. Structures et fichiers ▲
XI-A. Les types synonymes▲
Cette nouvelle notion de type synonyme va nous servir d'ici peu. Voyons de quoi il s'agit.
typedef
int
entier ;
définit un nouveau type appelé entier ayant les mêmes caractéristiques que le type prédéfini int. Une fois cette définition réalisée, nous pouvons utiliser ce nouveau type pour définir des variables et nous pouvons mélanger les variables de ce type avec des variables entières dans des expressions.
typedef
int
entier;
entier e1=
23
, e2=
5
, te[7
]={
1
,2
,3
,4
,5
,6
,7
}
;
int
i;
i =
e1 +
e2;
te[3
] =
i -
60
;
XI-B. Structures▲
Une structure est un objet composé de plusieurs champs qui sert à représenter un objet réel ou un concept. Par exemple une voiture peut être représentée par les renseignements suivants : la marque, la couleur, l'année, etc.
Solution 1 :
struct
nom_de_la_structure {
/* Définition de la structure */
}
nom_du_type;
Ceci fait, le nouveau type de données sera struct nom_du_type et nous pourrons déclarer une variable ainsi :
struct
nom_du_type nom_variable;
Cependant, la répétition du mot‐clé struct est rapidement ennuyeuse. Nous préférerons donc souvent la syntaxe suivante.
Solution 2 :
typedef
struct
{
/* Définition de la structure */
}
nom_du_type;
Cette fois‐ci, le nouveau type de données s'appelle nom_du_type (nous avons créé la structure et en même temps nous avons défini un synonyme avec typedef).
Nous déclarerons une variable ainsi :
nom_du_type nom_variable;
#define LONGUEUR 40
struct personne{
char nom [LONGUEUR];
char prenom [LONGUEUR];
int age;
};
struct personne p;
#define LONGUEUR 40
typedef
struct
{
char
nom [LONGUEUR];
char
prenom [LONGUEUR];
int
age;
}
personne;
personne p;
La seconde solution est plus simple et plus élégante à l'usage.
nom_de_variable.nom_du_champ
#include <stdio.h>
typedef
struct
{
char
nom [40
];
char
prenom [20
];
int
age;
}
personne;
int
main (
) {
personne p;
printf
(
"
Veuillez entrer le nom de la personne:
"
);
scanf
(
"
%s
"
,p.nom);
printf
(
"
Veuillez entrer le prénom de la personne:
"
);
scanf
(
"
%s
"
,p.prenom);
printf
(
"
Veuillez entrer l'âge de la personne:
"
);
scanf
(
"
%d
"
,&
p.age); /* ne pas oublier le & !!! */
printf
(
"
Voici les caractéristiques de cette personne:
\n
"
);
printf
(
"
nom=%s
\n
"
,p.nom);
printf
(
"
prenom=%s
\n
"
,p.prenom);
printf
(
"
age=%d
\n
"
,p.age);
return
0
;
}
XI-C. Bases sur les fichiers▲
Tout ce qui est enregistré sur votre disque dur ou presque est un fichier, et porte un nom.
Il est possible de créer, de lire ou d'écrire dans des fichiers. Notez que certains fichiers peuvent être protégés en lecture, en écriture ou les deux.
Voici un programme que nous allons détailler :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
#include <stdio.h>
#include <stdlib.h>
int
main (
) {
FILE *
p_fichier; /* pointeur sur fichier*/
char
nom_fichier[20
], nom_personne[20
];
int
i, nbr_enregistrements;
/* 1ère étape : Création et remplissage du fichier */
printf
(
"
Quel est le nom du fichier à créer ?
"
);
scanf
(
"
%s
"
, nom_fichier);
/* w: write r: read a: append*/
p_fichier =
fopen
(
nom_fichier, "
w
"
);
if
(
p_fichier ==
NULL
) {
printf
(
"
Erreur de création du fichier
\n
"
);
exit
(-
1
); // Abandonner le programme
}
printf
(
"
Nombre de personnes à stocker ? :
"
);
scanf
(
"
%d
"
, &
nbr_enregistrements);
for
(
i =
0
; i<
nbr_enregistrements; i++
) {
printf
(
"
Entrez le nom de la personne :
"
);
scanf
(
"
%s
"
, nom_personne);
fprintf
(
p_fichier, "
%s
\n
"
, nom_personne);
}
fclose
(
p_fichier);
/* 2ème étape : Lecture et affichage du fichier */
p_fichier =
fopen
(
nom_fichier,"
r
"
); /* read */
if
(
p_fichier ==
NULL
) {
printf
(
"
\a
Erreur d'ouverture sur le fichier
\n
"
);
exit
(-
2
); // Abandonner le programme
}
while
(!
feof
(
p_fichier)) {
fscanf
(
p_fichier, "
%s
"
, nom_personne);
printf
(
"
Nom : %s
\n
"
, nom_personne);
}
fclose
(
p_fichier);
return
0
;
}
Explications :
- Ligne 4 : une variable p_fichier est créée ; elle va pointer sur un type FILE. Sans entrer dans les détails, le type FILE est un type structure (vu au paragraphe précédent) qui permet de décrire un fichier.
- Ligne 9 : l'utilisateur va saisir une chaîne au clavier. Cette dernière sera stockée dans la variable nom_fichier. Supposons pour fixer les idées que l'utilisateur tape au clavier familles.txt. Le fichier qui sera par la suite créé portera ce nom.
- ligne 12 : fopen va créer une sorte de lien entre le fichier du disque dur qui s'intitule familles.txt et la variable p_fichier. Ainsi dans la suite, vous allez faire des opérations sur la variable p_fichier et toutes ces opérations seront répercutées au niveau du fichier familles.txt. Dans ce cas précis, les 3 opérations suivantes peuvent être réalisées :
- p_fichier=fopen(nom_fichier, "w"); : si le fichier familles.txtexiste déjà, il est purement et simplement écrasé puis réinitialisé à vide. S'il n'existe pas encore, le fichier est crée, pour l'instant il est vide.
- p_fichier=fopen(nom_fichier, "r"); : si le fichier familles.txtexiste déjà, il est simplement ouvert en lecture (read). L'ordinateur se positionne sur le premier caractère du fichier. Si le fichier n'existe pas (typiquement, nous nous sommes trompé de nom), la fonction fopen renvoie alors NULL.
- p_fichier=fopen(nom_fichier, "a"); : si le fichier familles.txtexiste déjà, il est simplement ouvert. Ensuite, l'ordinateur se positionne sur la fin de ce fichier, prêt à ajouter quelque chose après la dernière ligne. Nous comprenons mieux le "a" : append. Si le fichier n'existe pas, il est créé, et il est donc vide.
- Ligne 13 : il est toujours prudent de faire ce test. Le pointeur sera nul s'il y a eu un problème lors de l'accès au fichier (nom incorrect pour l'ouverture en lecture, accès en écriture impossible…)
- Ligne 15 : sortie catastrophe, le programme s'arrête immédiatement. La valeur ‐1 est renvoyée au système d'exploitation. Il est à noter que l'usage de la fonction exit impose d'ajouter la ligne #include <stdlib.h>.
- Ligne 23 : l'ordinateur lit au clavier le nom d'une personne.
- Ligne 24 : en fait, un fprintf n'est pas très différent d'un printf. La seule différence est qu'au lieu d'être écrite sur l'écran, la chaîne nom_personne sera écrite dans le fichier familles.txt.
- Ligne 26 : on referme le fichier pour indiquer au programme C que l'on a fini de travailler sur familles.txt pour l'instant. Il faut toujours penser à faire cette opération.
- Ligne 29 : ré‐ouverture du fichier, en lecture, cette fois‐ci. Si le fopen se passe bien (ce que nous pouvons supposer !), l'ordinateur se positionne alors au début de la 1re ligne du fichier.
- Ligne 34 : feof désigne l'abréviation de file end of file. Donc cette ligne se traduit par : tant que l'on n'atteint pas la fin du fichier désigné par p_fichier…
Enfin, voici une autre fonction qui peut se montrer très utile :
char
*
fgets
(
char
*
ligne, int
maxligne, FILE *
p_fichiers)
La fonction fgets lit à partir du fichier au maximum maxligne -1 caractères et les stocke dans la chaîne de caractères ligne.
La lecture s'arrête sur \n qui est alors inclus dans la chaîne. La chaîne est complétée par \0. La fonction renvoie NULL si la fin de fichier est atteinte.
#include <stdio.h>
/* Définition de constante */
#define maxligne 100
char
ligne[maxligne];
FILE *
p_fichier;
int
main
(
) {
p_fichier=
fopen
(
"
essai.txt
"
,"
r
"
);
while
(!
feof
(
p_fichier)) {
fgets
(
ligne,maxligne,p_fichier);
if
(!
feof
(
p_fichier))
printf
(
"
J'ai lu :%s
\n
"
,ligne);
}
fclose
(
p_fichier);
return
0
;
}
Le test suivant peut paraître curieux :
if
(!
feof
(
p_fichier))
printf
(
"
J'ai lu :%s
\n
"
,ligne);
En fait, il est nécessaire du fait que la fonction
feof
(
p_fichier)
renverra vrai si l'indicateur de fin de fichier du flux pfichier est positionné, c'est-à-dire s'il y a déjà eu une lecture infructueuse (par fgets par exemple).
Ainsi, lorsque le fgets lit la dernière ligne du fichier, un appel, dans la foulée à la fonction feof(p_fichier)renverra faux. Ce n'est que si nous refaisons un fgets (qui sera donc infructueux) que là, le test feof(p_fichier) renverra vrai. Donc finalement, nous voyons bien le problème : pour la toute dernière ligne, le fgets va échouer et l'instruction printf("J'ai lu :%s\n",lignes), si elle était appelée, pourrait bien renvoyer n'importe quoi !
XI-D. Fichiers et structures▲
Voici un exemple qui mêle fichiers et structures :
#include <stdio.h>
#include <stdlib.h>
typedef
struct
{
char
nom [40
];
char
prenom [20
];
int
age;
}
personne;
int
main
(
) {
FILE *
p_fichier; /* pointeur fichier */
/* Créer et remplir le fichier */
p_fichier =
fopen
(
"
essai.txt
"
,"
w
"
);
if
(
p_fichier ==
NULL
) {
printf
(
"
\a
Impossible de créer le fichier
\n
"
);
exit
(-
1
); // Abandonner le programme
}
personne p;
printf
(
"
Veuillez entrer le nom de la personne:
"
);
scanf
(
"
%s
"
,p.nom);
printf
(
"
Veuillez entrer le prénom de la personne:
"
);
scanf
(
"
%s
"
,p.prenom);
printf
(
"
Veuillez entrer l'âge de la personne:
"
);
scanf
(
"
%d
"
,&
p.age); /* ne pas oublier le & !!! */
fprintf
(
p_fichier, "
%s
\n
"
,p.nom);
fprintf
(
p_fichier, "
%s
\n
"
,p.prenom);
fprintf
(
p_fichier, "
%d
\n
"
,p.age);
fclose
(
p_fichier);
return
0
;
}