Jusqu’ici, toutes les interactions entre l’utilisateur et le programme en cours d’exécution se faisaient par le clavier pour les entrées, et par l’écran pour les sorties.
Cependant, les problèmes de calcul scientifique ne sont pas compatibles avec ce mode de fonctionnement. En général, on manipule de très grandes quantités de données, et on n’a absolument pas envie de devoir toutes les rentrer à la main, à chaque exécution du programme. Une seule erreur de saisie, et tout est fichu!
En plus, l’affichage à l’écran ne reste pas très longtemps disponible. Ce n’est pas une manière sûre de sauvegarder des résultats.
C’est pourquoi il est essentiel de savoir lire et écrire depuis des
fichiers. Mais pas de pression pour autant, si vous savez utiliser
std::cout
et std::cin
, vous avez déjà fait la
moitié du chemin.
std::cin
et std::cout
désignent des flux,
depuis l’entrée standard pour std::cin
, et depuis la sortie
standard pour std::cout
. (Il existe aussi
std::cerr
qui est un flux vers l’erreur standard, adaptée
aux messages d’erreurs)
De la même manière, on peut mettre en place des flux d’entrée (lecture) et de sortie (écriture) dans des fichiers. Pour chaque fichier, il faut créer le flux correspondant:
Ici, f1
est un objet de type std::ifstream
,
pour “input file stream”. f2
est un objet de type
std::ofstream
, pour “output file stream”.
Les types std::ifstream
et std::ofstream
sont définis dans la bibliothèque fstream
qu’il faut
inclure au début du fichier source (le
#include <fstream>
).
On va maintenant voir le fonctionnement basique de chacun de ces objets, en commençant par la lecture dans les fichiers.
std::ifstream
Quand on crée un flux de lecture depuis un fichier, celui-ci se
positionne au début du fichier. Prenons pour exemple le fichier
donnees.txt
ci-dessous:
Sur ces captures d’écran, j’indique où se situe le flux d’entrée en positionnant mon curseur dessus. Ici le flux est ligne 1, colonne 1 du fichier. (Les indices des lignes et colonnes de fichier commencent à 1, pas comme en C++)
Comme pour std::cin
, un std::ifstream
va
lire mot après mot (un mot = tous les caractères jusqu’à rencontrer un
espace, une tabulation, un retour à la ligne, ou la fin du fichier). Le
flux se place à chaque fois sur le mot suivant.
Ici la chaîne de caractères “12” est automatiquement convertie en
entier (le type de la variable i
). Le curseur est placé en
colonne 4, sur le mot suivant.
std::ifstream
On peut continuer la lecture…
Ici, notre fichier a la même structure sur chaque ligne : un entier, un réel et une chaîne de caractères. On peut utiliser une boucle pour stocker les valeurs, par exemple, en modifiant un peu le fichier d’entrées pour dire combien de lignes sont à lire:
#include <fstream> // entree/sortie dans les fichiers
#include <vector> // pour les tableaux
#include <iostream> // pour l'affichage a la fin
int main() {
std::ifstream f("donnees.txt");
std::vector<int> v1; std::vector<double> v2; std::vector<std::string> v3;
int nLignes;
// on obtient le nombre de lignes à lire
f >> nLignes;
// on lit ensuite chaque ligne
for (int i=0; i<nLignes; i++)
f >> v1[i] >> v2[i] >> v3[i];
std::cout << "Fichier lu correctement" << std::endl;
return 0;
}
std::ifstream
Il peut arriver que l’ouverture d’un fichier ne se passe pas bien. Par exemple, si le fichier dans lequel on veut lire n’existe pas !
Une méthode des std::ifstream
permet de vérifier l’état
du flux : good()
. Si good()
vaut
true
, cela veut dire qu’on peut continuer à lire dans le
fichier. Ça ne coûte pas grand chose de le rajouter dans les
programmes:
#include <fstream> // entree/sortie dans les fichiers
#include <vector> // pour les tableaux
#include <iostream> // pour l'affichage a la fin
int main() {
std::ifstream f("donnees.txt");
std::vector<int> v1; std::vector<double> v2; std::vector<std::string> v3;
int nLignes;
if (f.good()) {
// on obtient le nombre de lignes à lire
f >> nLignes;
// on lit ensuite chaque ligne
for (int i=0; i<nLignes; i++)
f >> v1[i] >> v2[i] >> v3[i];
std::cout << "Fichier lu correctement" << std::endl;
}
else {
std::cerr << "Erreur dans la lecture du fichier" << std::endl;
}
return 0;
}
Passons maintenant à l’écriture dans un fichier.
Quand on crée un objet de type std::ofstream
, le fichier
dont le nom est donné entre parenthèses sera créé.
Ensuite, le flux de sortie créé peut-être utilisé exactement comme
std::cout
.
std::ofstream f("resultats.dat");
int a(1); double b(10.e0); std::string c("coucou");
f << "On peut ecrire " << a << "dans le fichier" << std::endl;
f << "mais aussi " << b << " et " << c << std::endl;
Pour écrire des caractère spéciaux dans les flux de sortie, il faut utiliser des séquences d’échappement qui commencent par “\”. Elles sont listés sur cette page.