Jeux Libres
       
           

» Les Tutoriels » Apprenez à programmer en C++ ! » Lire et écrire des fichiers

Note : Vous vous apprêtez à lire un tutoriel de M@teo21 et Nanoc initialement publié à cette adresse sous la licence Creative Commons BY-NC-SA 2.0.

Lire et écrire des fichiers


Pour l'instant, les programmes que nous avons écrits étaient encore relativement simples. C'est normal, vous débutez. Mais avec un peu d'entrainement, vous seriez capable de créer de vraies applications. Vous commencez à connaître la base du C++. Il vous manque quand même un élément essentiel : l'interaction avec des fichiers.

Jusqu'à maintenant, vous avez appris à écrire dans la console et à récupérer ce que l'utilisateur saisit. Vous serez certainement d'accord avec moi, ce n'est pas suffisant. Pensez à des logiciels comme le bloc-note, votre IDE ou encore un tableur, ce sont tous des programmes qui savent lire et écrire des fichiers. Et même dans le monde des jeux vidéo, on a besoin de ça. Il y a bien sûr les fichiers de sauvegarde, mais aussi les images d'un jeu, les cinématiques, les musiques, etc. En somme, un programme qui ne sait pas interagir avec des fichiers risque d'être très limité.

Voyons donc comment faire ! Vous verrez, si vous maîtrisez l'utilisation de cin et de cout, alors vous savez déjà presque tout.





Chapitre précédent     Sommaire     Chapitre suivant


Écrire dans un fichier


La première chose à faire quand on veut manipuler des fichiers, c'est de les ouvrir. :soleil: Et bien en C++, c'est la même chose.
Une fois le fichier ouvert, tout se passe comme pour cout et cin. Nous allons, par exemple, retrouver les chevrons << et >>. Faites-moi confiance, vous allez rapidement vous y retrouver.

On parle de flux pour désigner les moyens de communication d'un programme avec l'extérieur. Dans ce chapitre, nous allons donc parler des flux vers les fichiers. Mais dites simplement "lire et écrire des fichiers" quand vous n'êtes pas dans une soirée de programmeurs. ;)

L'en-tête fstream



Comme d'habitude en C++, quand on a besoin d'une fonctionnalité, il faut commencer par inclure le bon fichier d'en-tête. Pour les fichiers, il faut spécifier #include <fstream> en-haut de notre code source.

Vous connaissez déjà iostream qui contient les outils nécessaires aux entrées/sorties vers la console. iostream signifie en réalité "input/output stream", ce qui veut dire flux d'entrées/sorties en français. fstream correspond à "file stream", flux vers les fichiers en bon français.


La principale différence est qu'il faut un flux par fichier. Voyons comment créer un flux sortant, c'est-à-dire un flux permettant d'écrire un fichier.

Ouvrir un fichier en écriture



Les flux sont en réalité des objets. Souvenez-vous que le C++ est un langage orienté objet. Voici donc un de ces fameux objets. :'(
N'ayez pas peur, il y aura plusieurs chapitres pour en parler. Pour l'instant, prenez-ça comme étant des grosses variables améliorées. Ces objets contiennent beaucoup d'informations sur les fichiers ouverts et proposent quelques fonctionnalités comme fermer le fichier, retourner au début et bien d'autres encore. :o

L'important pour nous est que l'on déclare un flux exactement de la même manière qu'une variable. Une variable dont le type serait ofstream.
1
2
3
4
5
6
7
8
9
#include <iostream>
#include <fstream>
using namespace std;
 
int main()
{
   ofstream monFlux;    //Dclaration d'un flux permettant d'crire un fichier.
   return 0;
}

A partir de là, il faut utiliser la fonction open() de l'objet pour ouvrir un fichier.
1
2
3
ofstream monFlux;    //Dclaration d'un flux permettant d'crire un fichier. 
 
monFlux.open("C:/Nanoc/scores.txt"); //Ouvre un fichier texte

J'ai indiqué le chemin d'accès du fichier entre guillemets. Ce chemin doit être d'une des deux formes suivantes :

  • Un chemin absolu. C'est-à-dire montrer l'emplacement du fichier depuis la racine du disque. Par exemple : C:/Nanoc/C++/Fichiers/scores.txt.
  • Un chemin relatif. C'est-à-dire montrer l'emplacement du fichier depuis l'endroit où se situe le programme sur le disque. Par exemple : Fichiers/scores.txt si mon programme se situe dans le dossier C:/Nanoc/C++/.

A partir de là, on peut utiliser le flux pour écrire dans le fichier.

Si le fichier n'existait pas, le programme le créerait automatiquement !


Le plus souvent, le nom du fichier est contenu dans une chaîne de caractères string. Dans ce cas, il faut utiliser la fonction c_str() lors de l'ouverture du fichier.
1
2
3
4
5
ofstream monFlux;    //Dclaration d'un flux permettant d'crire un fichier. 
 
string nomFichier("C:/Nanoc/scores.txt");
 
monFlux.open(nomFichier.c_str());

Des problèmes peuvent survenir lors de l'ouverture d'un fichier. Si le fichier ne vous appartient pas ou si le disque dur est plein par exemple. C'est pour ça qu'il faut toujours tester si tout c'est bien passé. Cela se fait en utilisant la syntaxe if(monFlux). Si ce test n'est pas vrai, alors c'est qu'il y a eu un problème et que l'on ne peut pas utiliser le fichier.
1
2
3
4
5
6
7
8
9
10
11
ofstream monFlux;
monFlux.open("C:/Nanoc/scores.txt"); //On essaye d'ouvrir le fichier
 
if(monFlux)    //On teste si tout est OK.
{
   //Tout est OK. On peut utiliser le fichier
}
else
{
   cout << "ERREUR: Impossible d'ouvrir le fichier." << endl;
}

Avant d'écrire, voyons quand même comment refermer un fichier. Et si vous parlez anglais, vous vous doutez peut-être que la fonction à utiliser se nomme simplement close().
1
2
3
4
5
6
7
8
9
10
11
12
ofstream monFlux;
monFlux.open("C:/Nanoc/scores.txt");  //On essaye d'ouvrir le fichier
 
if(monFlux)    
{
   //Tout est OK. On peut utiliser le fichier
}
else
{
   cout << "ERREUR: Impossible d'ouvrir le fichier." << endl;
}
monFlux.close(); //On finit par refermer le fichier

Voilà, vous savez tout ! Ah non, il manque encore l'écriture à proprement parler. lol

Écrire dans un flux



Je vous avais dit que tout était comme pour cout. C'est donc sans surprise que je vous présente le moyen d'envoyer des informations dans un flux. C'est les chevrons (<<) qu'il faut utiliser.
1
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
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
 
int main()
{
   ofstream monFlux;
   string const nomFichier("C:/Nanoc/scores.txt");
   monFlux.open(nomFichier.c_str());
 
   if(monFlux)    
   {
       monFlux << "Bonjour, je suis une phrase crite dans un fichier." << endl;
       monFlux << 42.1337 << endl;
       int age(23);
       monFlux << "J'ai " << age << " ans." << endl;
   }
   else
   {
       cout << "ERREUR: Impossible d'ouvrir le fichier." << endl;
   }
   monFlux.close();
 
   return 0;
}

Si j'exécute ce programme, je retrouve ensuite sur mon disque un fichier score.txt dont voici le contenu :


Essayez par vous-mêmes !
Vous pouvez par exemple écrire un programme qui demande son nom et son âge à l'utilisateur et qui écrit ces données dans un fichier.

Les différents modes d'ouverture



Il ne nous reste plus qu'un petit point à régler.

Que se passe-t-il si le fichier existe déjà ?


Il sera supprimé et remplacé par ce que vous écrivez, ce qui n'est pas bien si l'on souhaite ajouter des informations à la fin d'un fichier pré-existant. Pensez par exemple à un fichier qui contiendrait la liste des actions effectuées par l'utilisateur. On ne veut pas tout effacer à chaque fois. On veut juste y ajouter des lignes.

Pour pouvoir écrire à la fin d'un fichier, il faut le spécifier lors de l'ouverture en ajoutant un deuxième paramètre lors de l'appel à la fonction open() :
1
monFlux.open("C:/Nanoc/scores.txt", ios::app);

app est un raccourci pour append, le verbe anglais qui signifie "ajouter à la fin".

Avec ça, plus de problème d'écrasement des données. Tout ce qui sera écrit sera ajouté à la fin.


Lire un fichier


Nous avons appris à écrire dans un fichier, voyons maintenant comment fonctionne la lecture de fichier. Vous allez voir, ce n'est pas très différent de ce que vous connaissez déjà.

Ouvrir un fichier en lecture...



Le principe est exactement le même. On va simplement utiliser un ifstream au lieu d'un ofstream. Il faut également tester l'ouverture et on retrouve le close() pour la fermeture. C'est bien le C++ quand même. lol
1
2
3
4
5
6
7
8
9
10
11
12
13
ifstream monFlux;    //Un flux permettant de lire un fichier
monFlux.open("C:/Nanoc/C++/data.txt"); //Ouverture du fichier
 
if(monFlux)
{
   //Tout est prt pour la lecture.
}
else
{
   cout << "ERREUR: Impossible d'ouvrir le fichier en lecture." << endl;
}
 
monFlux.close()

Rien de bien nouveau. ;)

... et le lire



Il y a trois manières différentes de lire un fichier.

  • Ligne par ligne en utilisant getline().
  • Mot par mot en utilisant les chevrons >>.
  • Caractère par caractère en utilisant get().

Voyons ces trois moyens en détail.

Lire ligne par ligne



La première méthode permet de récupérer une ligne entière et de la stocker dans une chaîne de caractères.
1
2
string ligne;
getline(monFlux, ligne); //On lit une ligne complte

Le fonctionnement est exactement le même qu'avec cin. Vous savez donc déjà tout. :)

Lire mot par mot



La deuxième manière de faire, vous la connaissez aussi. Comme je suis gentil, je vous propose quand même un petit rappel.
1
2
3
4
double nombre;
monFlux >> nombre; //Lit un nombre virgule depuis le fichier
string mot;
monFlux >> mot; //Lit un mot depuis le fichier



Cette méthode lit ce qui se trouve entre l'endroit où l'on se situe dans le fichier et le prochain espace. Ce qui est lu est alors traduit en double, int ou string selon le type de variable dans lequel on écrit.

Lire caractère par caractère



Finalement, il nous reste la dernière méthode. La seule réellement nouvelle. Mais tout aussi simple, je vous rassure.
1
2
char a;
monFlux.get(a);

Ce code lit une seule lettre et la stocke dans la variable a.

Cette méthode lit réellement tous les caractères. Les espaces, retours à la ligne et tabulations sont, entre autres, lus par cette fonction. Bien que bizarres, ces caractères seront néanmoins stockées dans la variable.


Lire un fichier en entier



On veut très souvent lire un fichier en entier. Je vous ai montré comment lire, mais pas comment s'arrêter quand on arrive à la fin !

Pour savoir si l'on peut continuer à lire, il faut utiliser if(monFlux). Comme pour tester l'ouverture.
Vous vous rappelez des boucles ? On cherche à lire le fichier tant qu'on n'a pas atteint la fin. La boucle while est donc le meilleur choix. Voici comment faire :
1
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
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
 
int main()
{
   ifstream fichier;
   fichier.open("C:/Nanoc/fichier.txt");
 
   if(fichier)
   {
        //L'ouverture s'est bien passe. On peut donc lire
 
       while(fichier)    //Tant qu'on n'est pas a la fin
       {
           string ligne;
           getline(fichier, ligne); //On lit une ligne
   
           cout << ligne << endl; //Et on l'affiche dans la console
       }
   }
   else
   {
       cout << "ERREUR: Impossible d'ouvrir le fichier en lecture." << endl;
   }
   
   fichier.close();
 
   return 0;
}

Une fois que l'on a lu les lignes, on peut les manipuler facilement. Ici, j'affiche simplement les lignes, mais dans un programme réel on les utiliserait autrement. La seule limite est votre imagination. ;)


Quelques astuces


Il ne reste que quelques astuces à voir et vous saurez alors tout ce qu'il faut sur les fichiers. :)

Gagner de la place



Je vous l'ai souvent dit, la principale qualité du programmeur est la fainéantise. Voici donc deux petites astuces pour écrire moins de lignes.

La première astuce permet de déclarer le flux et d'ouvrir le fichier en une seule ligne.

Le morceau de code suivant :
1
2
ofstream flux;
flux.open("C:/Nanoc/fichier.txt");

... peut être remplacé par son équivalent plus court :
1
ofstream flux("C:/Nanoc/fichier.txt");

On a gagné une ligne. Il n'y a aucune différence entre les deux variantes. La deuxième est juste plus courte.

L'autre élément qui peut être supprimé est l'appel à la fonction close(). En effet, le fichier sera automatiquement fermé à la fin de la fonction où le flux est déclaré. Cela nous fait donc une deuxième ligne de gagnée. :)

Beaucoup de gens aiment bien garder le open() et le close(). On peut ainsi mieux voir où le fichier est ouvert et où il est refermé. C'est une question de goût. A vous de voir ce que vous préférez.


Le curseur dans le fichier



Plongeons un petit peu plus dans les détails techniques. Voyons comment se déroule la lecture. Quand on ouvre un fichier dans le bloc-note, par exemple, il y a un curseur qui indique l'endroit où l'on va écrire. Sur l'image suivante, le curseur se situe après les deux "s" sur la 4e ligne.


Si l'on tape sur une touche du clavier, une lettre sera ajoutée à cet endroit du fichier. J'imagine que je ne vous apprends rien en disant ça. ;) Ce qui est plus intéressant, c'est qu'en C++ il y a aussi en quelque sorte un curseur.

Lorsque l'on écrit la ligne suivante :
1
ifstream fichier("C:/Nanoc/scores.txt");

le fichier C:/Nanoc/scores.txt est ouvert et le curseur est placé tout au début du fichier. Si on lit le premier mot du fichier, on obtient bien sûr la chaîne de caractères "Nanoc" puisque c'est le premier mot du fichier. En faisant ça, le "curseur C++" se déplace jusqu'au début du mot suivant. Comme sur l'image suivante :


Le mot suivant qui pourra être lu sera donc ":", puis 118218 et ainsi de suite jusqu'à la fin. On est donc obligés de lire un fichier dans l'ordre. Ce n'est pas très pratique. :(

Heureusement, il existe des moyens de se déplacer dans un fichier. On peut par exemple dire "je veux placer le curseur 20 caractères après le début" ou "je veux avancer le curseur de 32 lettres". On peut ainsi lire que les parties qui nous intéressent réellement.

La première chose à faire est de savoir où se situe le curseur. Dans un deuxième temps, on pourra le déplacer. Voici comment.

Connaître sa position



Il existe une fonction permettant de savoir à quel octet du fichier on se trouve. Autrement dit, elle permet de savoir à quel caractère du fichier on se situe. Malheureusement, cette fonction n'a pas le même nom pour les flux entrants et sortants. Et en plus ce sont des noms bizarres. o_O Je vous ai mis les noms des deux fonctions dans un petit tableau.

Pour ifstreamPour ofstream
teelg()
teelp()

Par contre, elles s'utilisent toutes les deux de la même manière.
1
2
3
4
5
6
ofstream fichier;
fichier.open("C:/Nanoc/data.txt");
 
int position = fichier.tellp(); //On recupere la position
 
cout << "Nous nous situons au " << position << "eme caractere du fichier." << endl;

Se déplacer



A nouveau, il existe deux fonctions. Une pour chaque type de flux.

Pour ifstreamPour ofstream
seekg()
seekp()

Elles s'utilisent à nouveau de la même manière, je ne vous présente donc qu'une des deux versions.

Ces fonctions reçoivent deux arguments. Une position dans le fichier et un nombre de caractères à ajouter à cette position :
1
flux.seekp(nombreCaracteres, position);

Les trois positions possibles sont :

  • Le début du fichier : ios::beg.
  • La fin du fichier : ios::end.
  • La position actuelle : ios::cur.

Si par exemple, je souhaite me placer 10 caractères après le début du fichier, j'utilise flux.seekp(10, ios::beg);. Si je souhaite aller 20 caractères plus loin que l'endroit où le curseur se situe, j'utilise flux.seekp(20, ios::cur);.
Je pense que vous avez compris. ;)

Voilà donc notre problème de lecture résolu.

Connaître la taille d'un fichier



Cette troisième astuce utilise en réalité les deux précédentes. Pour connaître la taille d'un fichier, on se déplace à la fin et on demande au flux de nous dire où il se trouve. Vous voyez comment faire ? :euh:
Bon, je vous montre.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <fstream>
using namespace std;
 
int main()
{
   ifstream fichier("C:/Nanoc/meilleursScores.txt"); //On ouvre le fichier
   fichier.seekp(0, ios::end); //On se deplace a la fin du fichier
 
   int taille;
   taille = fichier.tellp();  //On recupere la position qui correspond donc a la taille du fichier !
 
   cout << "Taille du fichier : " << taille << " octets." << endl;
 
   //Pas besoin d'appeler fichier.close().
   //Souvenez-vous de la premiere astuce !
 
   return 0;
}

Je suis sûr que vous le saviez !

Voilà, on a fait le tour des notions principales. Vous êtes prêt à vous lancer seul dans le vaste monde des fichiers.




Avec ces nouvelles notions, vous êtes prêts pour entrer dans la cour des grands et réaliser de vrais programmes. Et ça tombe bien, puisque le prochain chapitre sera un TP dans lequel vous aurez besoin de tout ce que vous avez appris précédemment !

Je vous conseille de bien réviser les parties qui vous semblaient compliquées !



Chapitre précédent     Sommaire     Chapitre suivant



Distribué et adapté par David
Consulté 14303 fois



Hébergeur du site : David
Version PHP : 5.4.45-0+deb7u2
Uptime : 354 jours 21 heures 47 minutes
Espace libre : 1514 Mo
Dernière sauvegarde : 13/11/2019
Taille de la sauvegarde : 1115 Mo


5644949 pages ont été consultées sur le site !
Dont 3163 pages pendant les 24 dernières heures.

Page générée en 0.658 secondes


Nos sites préférés
- Création d'un jeu de plateforme de A à Z avec SDL
- Zelda ROTH : Jeux amateurs sur le thème de Zelda
- Zeste de Savoir : la connaissance pour tous et sans pépins
- YunoHost : s'héberger soi-même en toute simplicité
- Site de Fvirtman : recueil de projets et de codes en C et C++
- Par ici la sortie : le site des idées de sorties


  © 2005-2019 linor.fr - Toute reproduction totale ou partielle du contenu de ce site est strictement interdite.