Vous êtes sur la page 1sur 23

Chapitre 4

Les Fichiers
I- Les opérations sur les fichiers
Un fichier est un ensemble de données généralement stockées sur le disque d’un ordinateur.
Les données peuvent être enregistrées dans des fichiers, puis réutilisées ultérieurement.
Presque tous les programmes du monde réel utilisent des fichiers pour stocker et récupérer des
données. Voici quelques exemples de progiciels familiers qui utilisent beaucoup les fichiers.
• Traitement de texte : les programmes de traitement de texte sont utilisés pour écrire des
lettres, des mémos, des rapports et d'autres documents. Les documents sont ensuite
enregistrés dans des fichiers pour pouvoir être modifiés et réimprimés.
• Systèmes de gestion de bases de données : les SGBD sont utilisés pour créer et maintenir
des bases de données. Les bases de données sont des fichiers qui contiennent de grandes
collections de données, telles que les enregistrements de paie, les inventaires, les
statistiques des ventes et les enregistrements des clients.
• Feuilles de calcul : les programmes de feuilles de calcul sont utilisés pour travailler avec
des données numériques. Des nombres et des formules mathématiques peuvent être
insérés dans les lignes et les colonnes de la feuille de calcul. La feuille de calcul peut
ensuite être enregistrée dans un fichier pour une utilisation ultérieure.
• Compilateurs : les compilateurs traduisent le code source d'un programme, qui est
enregistré dans un fichier, en un fichier exécutable.

Type de Description
données
ifstream Flux d’entrée de fichiers. Ce type de données ne peut être utilisé que pour
lire les données des fichiers en mémoire.
ofstream Flux de sortie de fichiers. Ce type de données peut être utilisé pour créer des
fichiers et y écrire des données.
fstream Flux de fichiers. Ce type de données peut être utilisé pour créer des fichiers,
y écrire des données et en lire des données.
1- Utilisation du type de données fstream

On définit un objet fstream comme on définit des objets d'autres types de données. L'instruction
suivante définit un objet fstream nommé nouveauFichier.
fstream nouveauFichier;
Comme pour les objets ifstream et ofstream, vous utilisez la fonction open() d'un objet fstream
pour ouvrir un fichier. La fonction ouverte d'un objet fstream nécessite cependant deux
arguments :
• Le premier argument est une chaîne contenant le nom du fichier ;
• Le deuxième argument est un accès au fichier, indicateur qui indique le mode dans
lequel vous souhaitez ouvrir le fichier. Voici un exemple.
nouveauFichier.open ("Dassy.txt", ios ::out);
Le premier argument de cet appel de fonction est le nom du fichier, Dassy.txt. Le deuxième
argument est l'indicateur d'accès au fichier ios::out. Cela indique à C++ d'ouvrir le fichier en
mode de sortie. Le mode de sortie permet d'écrire des données dans un fichier. L'instruction
suivante utilise l'indicateur d'accès ios::in pour ouvrir un fichier en mode d'entrée, ce qui permet
la lecture des données à partir du fichier.
nouveauFichier.open ("Dassy.txt", ios::in);
Il existe de nombreux indicateurs d'accès aux fichiers, comme indiqué dans le tableau ci-
dessous :

Indicateur d'accès aux Description


fichiers
ios::app(app_end) Le mode Append. Si le fichier existe déjà, son contenu est
conservé et toute la sortie est écrite à la fin du fichier. Par défaut,
cet indicateur entraîne la création du fichier s'il n'existe pas.
ios::ate(at_end) Si le fichier existe déjà, le programme va directement à la fin de
celui-ci. La sortie peut être écrite n'importe où dans le fichier.
ios::binary Le mode binary. Lorsqu'un fichier est ouvert en mode binaire, les
données y sont écrites ou lues au format binaire pur. (Le mode par
défaut est le texte.)
ios::in(input) Le mode input. Les données du fichier seront lues. Si le fichier
n'existe pas, il ne sera pas créé et la fonction d'ouverture échouera
ios::out(output) Le mode output. Les données seront écrites dans le fichier. Par
défaut, le contenu du fichier sera supprimé s’il existe déjà.
ios::trunc(trunc_ate) Si le fichier existe déjà, son contenu sera supprimé (tronqué). C'est
le mode par défaut utilisé par ios::out.

Plusieurs indicateurs peuvent être utilisés ensemble. Il suffit de les relier avec l’opérateur | . Par
exemple, supposons que nouveauFichier est un objet fstream dans l'instruction suivante :
nouveauFichier.open ("Dassy.txt", ios::in | ios::out);
Cette instruction ouvre le fichier Dassy.txt dans les modes d'entrée et de sortie. Cela signifie
que les données peuvent être écrites et lues à partir du fichier.

IMPORTANT :

Lorsqu'il est utilisé seul, l'indicateur ios::out provoque la suppression du contenu du fichier si le
fichier existe déjà. Cependant, lorsqu'il est utilisé avec l'indicateur ios::in, le contenu existant du
fichier est conservé. Si le fichier n'existe pas déjà, il sera créé.

L'instruction suivante ouvre le fichier de telle manière que les données ne seront écrites qu'à la
fin :
nouveauFichier.open ("Dassy.txt", ios::out | ios::app);
En utilisant différentes combinaisons d'indicateurs d'accès, vous pouvez ouvrir des fichiers dans
de nombreux modes possibles.
Le programme ci-dessous utilise un objet fstream pour ouvrir un fichier pour la sortie, puis écrit
des données dans le fichier.
Le contenu réel du fichier apparaît sous la forme d'un flux de caractères, comme illustré dans
la Figure ci-dessous :

<E
\ \ \
M C H A T C H U I N M D J O K O M D A S S Y OF
n n n
>

Comme vous pouvez le voir sur la figure, les caractères \n sont écrits dans le fichier avec tous
les autres caractères. Les caractères sont ajoutés au fichier de manière séquentielle, dans l'ordre
où ils sont écrits dans le programme. Le tout dernier caractère est un marqueur de fin de fichier.
C'est un caractère qui marque la fin du fichier et est automatiquement écrit lorsque le fichier est
fermé. (Le caractère réel utilisé pour marquer la fin d'un fichier dépend du système
d'exploitation utilisé. Il s'agit toujours d'un caractère non imprimable. Par exemple, certains
systèmes utilisent control-Z.)
Le programme ci-dessous est une modification du programme ci-dessus qui illustre davantage
la nature séquentielle des fichiers. Le fichier est ouvert, trois noms y sont écrits et il est fermé.
Le fichier est ensuite réouvert par le programme en mode append ou ajout (avec l'indicateur
d’accès ios::app). Lorsqu'un fichier est ouvert en mode append ou ajout, son contenu est
conservé et toutes les sorties ultérieures sont ajoutées à l’envoi du fichier. Deux autres noms
sont ajoutés au fichier avant qu'il ne soit fermé et que le programme se termine.

Si l'indicateur ios::out avait été seul, sans ios::app la deuxième fois que le fichier a été ouvert,
le contenu du fichier aurait été supprimé. Si tel avait été le cas, les noms M CHATCHIN,
DJOKO et Dassy auraient été effacés, et le fichier n'aurait contenu que les noms DEFO et
BANSI.

2- Modes d'ouverture de fichier avec les objets ifstream et ofstream


Les types de données ifstream et ofstream ont chacun un mode par défaut dans lequel ils
ouvrent les fichiers. Ce mode détermine les opérations qui peuvent être effectuées sur le fichier
et ce qui se passe si le fichier en cours d'ouverture existe déjà. Le tableau ci-dessous décrit le
mode d’ouverture par défaut de chaque type de données.

Type de Mode d'ouverture par défaut


fichier
ofstream Le fichier est ouvert uniquement pour la sortie. Les données peuvent être écrites
dans le fichier, mais pas lues à partir du fichier. Si le fichier n'existe pas, il est
créé. Si le fichier existe déjà, son contenu est supprimé (le fichier est tronqué).
ifstream Le fichier est ouvert pour saisie uniquement. Les données peuvent être lues à
partir du fichier, mais pas écrites dessus. Le contenu du fichier sera lu depuis
son début. Si le fichier n'existe pas, la fonction d'ouverture échoue.

Vous ne pouvez pas modifier le fait que les fichiers ifstream ne peuvent qu’être lus et les
fichiers ofstream ne peuvent qu’être écrits. Vous pouvez toutefois modifier la manière dont les
opérations sont effectuées sur ces fichiers en fournissant un indicateur d'accès aux fichiers
comme second argument facultatif de la fonction d'ouverture. Le code suivant montre un
exemple utilisant un objet ofstream.
ofstream ecrireFichier;
ecrireFile.open ("Dassy.txt", ios::out | ios::app);
L'indicateur ios::app spécifie que les données écrites dans le fichier Dassy.txt doivent être
ajoutées à son contenu existant.

3- Vérification de l’existence d’un fichier avant son ouverture


Parfois, vous souhaitez savoir si un fichier existe déjà avant de l'ouvrir pour la sortie. Vous
pouvez le faire en essayant d'abord d'ouvrir le fichier pour l'entrée. Si le fichier n'existe pas,
l'opération d'ouverture échouera. Dans ce cas, vous pouvez créer le fichier en l'ouvrant pour la
sortie. Le code suivant donne un exemple
4- Ouverture d'un fichier avec l'instruction de définition d'objet de flux
de fichiers
Une alternative à l'utilisation de la fonction membre d'ouverture consiste à utiliser l'instruction
de définition d'objet de flux de fichiers pour ouvrir le fichier. Voici un exemple :
fstream nouveauFichier ("Dassy.txt", ios :: in | ios :: out);
Cette instruction définit un objet fstream nommé nouveauFichier et l'utilise pour ouvrir le
fichier Dassy.txt. Le fichier est ouvert dans les deux modes d'entrée et de sortie. Cette technique
élimine le besoin d'appeler la fonction open() lorsque votre programme connaît le nom et le
mode d'accès du fichier au moment où l'objet est défini. Vous pouvez également utiliser cette
technique avec des objets ifstream et ofstream, comme illustré dans les exemples suivants.

ifstream lireFichier("DassyL.txt");
ofstream ecrireFichier("DassyE.txt");
ofstream nouveauFichier("DassyLE.txt", ios::out|ios::app);

Vous pouvez également tester les erreurs après avoir ouvert un fichier avec cette technique. Le
code suivant montre un exemple.
ifstream lireFichier ("Dassy.txt");
if (! lireFile)
cout << "Erreur lors de l'ouverture de Dassy.txt. \n";

II- Formatage de la sortie d’un fichier


La sortie de fichier peut être formatée de la même manière que la sortie d'écran est formatée.
Les techniques de formatage de sortie est la même que celles utilisées avec cout. Exemple :
Notez que la sortie du fichier est formatée comme cout le ferait pour la sortie d'écran. Le
programme ci-dessous montre le manipulateur de flux setw utilisé pour formater la sortie du
fichier en colonnes.
La figure ci-dessus montre la façon dont les caractères apparaissent dans le fichier.

III- Passage d'objets de flux de fichiers à des fonctions


Les objets de flux de fichiers peuvent être passés par référence à des fonctions. Lors de l'écriture
de programmes réels, vous souhaiterez créer un code modulaire pour gérer les opérations sur
les fichiers. Les objets de flux de fichiers peuvent être passés aux fonctions, mais ils doivent
toujours être passés par référence. La fonction ouvrirFichier() illustrée ci-dessous utilise un
paramètre d'objet de référence fstream :

L'état interne des objets de flux de fichiers change à chaque opération. Ils doivent toujours être
transmis aux fonctions par référence pour garantir la cohérence interne. Le programme 12-5
montre un exemple de la manière dont les objets de flux de fichiers peuvent être passés comme
arguments aux fonctions.
IV- Test d'erreur plus détaillé

Tous les objets de flux ont des bits d'état d'erreur qui indiquent l'état du flux.
Tous les objets de flux contiennent un ensemble de bits qui agissent comme des indicateurs.
Ces indicateurs indiquent l'état actuel du flux. Le tableau ci-dessous répertorie ces bits.
Ces bits peuvent être testés par les fonctions membres répertoriées dans le Tableau
ofstream/ifstream ci-dessus. (Vous avez déjà découvert la fonction fail ().) L'une des fonctions
répertoriées dans le tableau, clear (), peut être utilisée pour définir un bit d'état.

Bit Description
ios::eofbit Défini lorsque la fin d'un flux d'entrée est rencontrée.
ios::failbit Défini lorsqu'une tentative d'opération a échoué.
ios::hardfail Défini lorsqu'une erreur irrémédiable s'est produite.
ios::badbit Défini lorsqu'une opération non valide a été tentée.
ios::goodbit Défini lorsque tous les indicateurs ci-dessus ne sont pas définis. Indique que
le flux est en bon état.

Founction Description
eof() Renvoie true (nonzero) si l'indicateur eofbit est défini, sinon renvoie false.
fail() Renvoie true (différent de zéro) si les indicateurs failbit ou hardfail sont définis,
sinon renvoie false.
bad() Renvoie vrai (différent de zéro) si l'indicateur de badbit est défini, sinon renvoie
faux.
good() Renvoie true (différent de zéro) si l'indicateur goodbit est défini, sinon renvoie
false.
clear() Lorsqu'il est appelé sans argument, efface tous les indicateurs répertoriés ci-
dessus. Peut également être appelé avec un indicateur spécifique comme
argument.
La fonction montreStatus, illustrée ici, accepte une référence de flux de fichier comme
argument. Il montre l'état du fichier en affichant les valeurs de retour des fonctions membres
eof (), fail (), bad () et good ():

Le programme ci-dessous utilise la fonction montreStatus() pour afficher l’état de testFichier


après diverses opérations. Tout d'abord, le fichier est créé et la valeur entière 10 y est stockée.
Le fichier est ensuite fermé et réouvert pour saisie. L'entier est lu dans le fichier, puis une
seconde opération de lecture est effectuée. Comme il n'y a qu'un seul élément dans le fichier, la
deuxième opération de lecture entraînera une erreur.
V- Les fonctions membres pour la lecture et l'écriture dans les
fichiers
Les objets de flux de fichiers ont des fonctions membres plus spécialisées pour la lecture et
l'écriture dans les fichiers
Si des caractères d'espacement font partie des données d'un fichier, un problème survient
lorsque le fichier est lu par l'opérateur >>. Étant donné que l'opérateur considère les caractères
d'espacement comme des délimiteurs, il ne les lit pas. Par exemple, considérons le fichier
Dassy.txt, qui contient les données suivantes :

CHATCHUIN DJOKO DASSY

Santa Barbara

698086055
s
La figure ci-dessous montre la manière dont les données sont enregistrées dans le fichier.

C H A T C H U I N

D J O K O D A S S

Y \n S a n t a B a
r
b a r a \n 6 9 8 0 8

6 0 5 5 \n <EOF>

Le problème qui découle de l'utilisation de l'opérateur >> est évident dans la sortie du
programme.
1- La fonction getline()
Le problème avec le programme ci-dessus peut être résolu en utilisant la fonction getline(). La
fonction lit une « ligne » de données, y compris des espaces blancs. Voici un exemple de l'appel
de fonction :
getline(nomFichier, str,'\n');
Les trois arguments de cette déclaration sont expliqués comme suit :
• nomFichier : Il s'agit du nom de l'objet de flux de fichiers. Il spécifie l'objet de flux à
partir duquel les données doivent être lues.
• Str : Il s'agit du nom d'un objet chaîne. Les données lues dans le fichier seront stockées
ici.
• '\n' : Ceci est un caractère de délimitation de votre choix. Si ce délimiteur est rencontré,
la fonction arrêtera la lecture. (Cet argument est facultatif. S'il est omis, '\ n' est la valeur
par défaut.)

L'instruction lit une ligne de caractères du fichier. La fonction lira jusqu'à ce qu'elle rencontre
le caractère \n. La ligne de caractères sera stockée dans l'objet str.
Le programme dessous est une modification du programme écrit ci-dessus. Il utilise la fonction
getline() pour lire des lignes entières de données du fichier.
~TP~

Notez que les caractères \n, qui marquent la fin de chaque enregistrement, font également partie
de la sortie. Ils provoquent l'impression d'une ligne vierge supplémentaire à l'écran, séparant les
enregistrements.
REMARQUE : lorsque vous utilisez un caractère imprimable, tel que $, pour délimiter des
données dans un fichier, veillez à sélectionner un caractère qui n'apparaîtra pas réellement dans
les données elles-mêmes. Comme il est douteux que le nom ou l’adresse d’une personne
contienne un caractère $, il s’agit d’un délimiteur acceptable. Si le fichier contenait des
montants en dollars, cependant, un autre délimiteur aurait été choisi.
2- La fonction de membre get()
La fonction membre get() de l’objet de flux de fichiers est également utile. Il lit un seul caractère
du fichier. Voici un exemple de son utilisation :
inFichier.get (ch);
Dans cet exemple, ch est une variable char. Un caractère sera lu dans le fichier et stocké dans
ch. Le programme ci-dessous montre la fonction utilisée dans un programme complet.
L'utilisateur est invité à saisir le nom d'un fichier. Le fichier est ouvert et la fonction get est
utilisée dans une boucle pour lire le contenu du fichier, un caractère à la fois.
Remarque :

La fonction get() lit même les espaces, donc tous les caractères seront affichés
exactement tels qu'ils apparaissent dans le fichier.

3- La fonction de membre put()


La fonction membre put écrit un seul caractère dans le fichier. Voici un exemple de son
utilisation :
ecrireFichier.put (ch);
Dans cette instruction, la variable ch est supposée être une variable char. Son contenu sera écrit
dans le fichier associé à l'objet de flux de fichiers ecrireFichier. Le programme ci-dessous
montre la fonction put().
VI- Fichiers à accès aléatoire
L'accès aléatoire signifie l'accès non séquentiel aux données d'un fichier.
Tous les programmes créés jusqu'à présent dans ce chapitre ont effectué un accès séquentiel
aux fichiers. Lorsqu'un fichier est ouvert, la position où la lecture et/ou l'écriture auront lieu est
au début du fichier (sauf si le mode ios::app est utilisé, ce qui entraîne l'écriture des données à
la fin du fichier). Si le fichier est ouvert pour la sortie, les octets y sont écrits les uns après les
autres. Si le fichier est ouvert en entrée, les données sont lues en commençant au premier octet.
Au fur et à mesure que la lecture ou l’écriture se poursuit, la position de lecture / écriture de
l’objet de flux de fichier avance séquentiellement dans le contenu du fichier.
Le problème avec l'accès séquentiel aux fichiers est que pour lire un octet spécifique du fichier,
tous les octets qui le précèdent doivent être lus en premier. Par exemple, si un programme a
besoin de stocker des données au centième octet d'un fichier, il devra lire les 99 premiers octets
pour y accéder. Si vous avez déjà écouté un lecteur de cassette, vous comprenez l’accès
séquentiel. Pour écouter une chanson à la fin de la cassette, vous devez écouter toutes les
chansons qui la précèdent, ou avancer rapidement dessus. Il n'y a aucun moyen de passer
immédiatement à cette chanson en particulier.

Bien que l'accès séquentiel aux fichiers soit utile dans de nombreuses circonstances, il peut
ralentir considérablement un programme. Si le fichier est très volumineux, la localisation des
données enfouies profondément à l'intérieur peut prendre beaucoup de temps. Alternativement,
C++ permet à un programme d'effectuer un accès aléatoire aux fichiers. En accès aléatoire aux
fichiers, un programme peut sauter immédiatement à n'importe quel octet du fichier sans avoir
à lire d'abord les octets précédents. La différence entre l'accès séquentiel et aléatoire aux fichiers
est comme la différence entre une cassette et un disque compact. Lors de l'écoute d'un CD, on
n'a pas besoin d'écouter ou d'avancer rapidement sur des chansons indésirables. Vous passez
simplement à la piste que vous souhaitez écouter.

Accès
séquentiel :

Accès
aléatoire :

1- Les fonctions membres seekp() et seekg()


Les objets de flux de fichiers ont deux fonctions membres qui sont utilisées pour déplacer la
position de lecture/écriture vers n'importe quel octet du fichier.
• seekp ;
• seekg.
La fonction seekp est utilisée avec les fichiers ouverts pour la sortie, et seekg est utilisé avec
les fichiers ouverts pour l'entrée. (Cela a du sens si vous vous souvenez que "p" signifie "put"
et "g" signifie "get". Seekp est utilisé avec les fichiers dans lesquels vous mettez des données,
et seekg est utilisé avec les fichiers dont vous récupérez les données.

Voici un exemple d'utilisation de seekp:


fichier.seekp (20L, ios :: beg);
Le premier argument est un entier long représentant un décalage dans le fichier. Il s'agit du
numéro de l'octet vers lequel vous souhaitez vous déplacer. Dans cet exemple, 20L est utilisé.
(N'oubliez pas que le suffixe L force le compilateur à traiter le nombre comme un entier long.)
Cette instruction déplace la position d'écriture du fichier vers l'octet numéro 20. (Toute
numérotation commence à 0, donc l'octet numéro 20 est en fait le vingt et unième octet. )
Le deuxième argument s'appelle le mode et désigne à partir duquel calculer le décalage.
L'indicateur ios::beg signifie que le décalage est calculé à partir du début du fichier.
Alternativement, le décalage peut être calculé à partir de la fin du fichier ou de la position
actuelle dans le fichier. Le tableau ci-dessous répertorie les indicateurs pour les trois modes
d'accès aléatoire.
Mode de Drapeau Description
ios::beg Le décalage est calculé à partir du début du fichier.
ios::end Le décalage est calculé à partir de la fin du fichier.
ios::cur Le décalage est calculé à partir de la position actuelle.

~TP~

2- Les fonctions membres tellp() et tellg()


Les objets de flux de fichiers ont deux autres fonctions membres qui peuvent être utilisés pour
l'accès aléatoire aux fichiers : tellp et tellg. Leur objectif est de renvoyer, sous forme d'entier
long, le numéro d'octet actuel de la position de lecture et d'écriture d'un fichier. Comme vous
pouvez le deviner, tellp retourne la position d'écriture et tellg la position de lecture. En
supposant que pos est un entier long, voici un exemple d'utilisation des fonctions :
pos = ecrireFichier.tellp ();
pos = lireFichier.tellg ();
Une application de ces fonctions consiste à déterminer le nombre d'octets que contient un
fichier.
L'exemple suivant montre comment procéder à l'aide de la fonction tellg.
fichier.seekg (0L, ios :: end);
nombreDeBit = fichier.tellg ();
cout << "Le fichier contient" << nombreDeBit << "octets. \ n";
Tout d'abord, la fonction membre seekg est utilisée pour déplacer la position de lecture vers le
dernier octet du fichier. Ensuite, la fonction tellg est utilisée pour obtenir le numéro d'octet
actuel de la position de lecture.

~TP~

Vous aimerez peut-être aussi