Chapitre 3 – Les entrées et sorties : flux, console et fichiers
Document réservé
Vous consultez actuellement la présentation publique de ce chapitre.
Les documents PDF complets, comprenant les développements théoriques, les exemples détaillés et les exercices, sont disponibles sur demande.
Pour obtenir un accès, contactez-moi via la page Contact en indiquant les domaines qui vous intéressent (C++, ESP-IDF, électronique, etc.).
Source PDF : P3-Chap-03.pdf
(Limite 50000 octets — symratio=0.40, minlen=20, clean)
3 Les entrées et sorties : flux, console et fichiers 3.4 La sortie standard avec std : :cout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.5 L’entrée standard avec std : :cin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.6 Une première difficulté : les espaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.7 Le piège de std : :getline après std : :cin . . . . . . . . . . . . . . . . . . . . . . . . . . 3.8 std : :endl et le caractère \n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.9 Les flux utilisent des tampons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.11 Le formatage des nombres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.12 Pourquoi utiliser des fichiers ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.13 Écrire dans un fichier avec std : :ofstream . . . . . . . . . . . . . . . . . . . . . . . . . 3.14 Vérifier l’ouverture du fichier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.15 Écrire une série de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.16 Faut-il fermer explicitement le fichier ? . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.17 Lire un fichier avec std::ifstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.19 Pourquoi ne pas utiliser while ( !fichier.eof()) ? . . . . . . . . . . . . . . . . . . . . . . 3.20 Lire un fichier ligne par ligne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.21 Remplacer ou compléter un fichier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.22 std::fstream - lecture et écriture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.27 Une même abstraction pour plusieurs destinations . . . . . . . . . . . . . . . . . . . . 2026 - C++ Partie III Les entrées et sorties : flux, console et fichiersAccueilLes entrées et sorties : flux, console Jusqu’à présent, nos programmes ont principalement travaillé avec des données présentes en mémoire, ils ont créé des variables, effectué des calculs, manipulé des objets et affiché certains résultats à l’écran. Cependant, un programme réel doit pouvoir communiquer avec l’extérieur afin de pouvoir : • recevoir des informations de l’utilisateur ; • afficher des résultats ; • lire des données enregistrées dans un fichier ; • sauvegarder des résultats pour une utilisation ultérieure ; • transmettre des données à un autre programme. Toutes ces opérations appartiennent à la famille des entrées/sorties, souvent abrégée E/S, ou I/O en anglais pour Input/Output. Le C++ organise une grande partie de ces opérations autour d’une notion fondamentale le flux qui permet d’utiliser des mécanismes très similaires pour communiquer avec le clavier, l’écran ou un fichier. Qu’est-ce qu’un flux ? Un flux peut être vu comme une succession de données circulant entre un programme et une source ou une destination extérieure. Dans un flux d’entrée, les données arrivent vers le programme, par clavier −→ programme Dans un flux de sortie, les données quittent le programme, programme −→ écran La même idée s’applique aux fichiers : fichier −→ programme programme −→ fichier Le programme n’a donc pas nécessairement besoin de connaître tous les détails du périphérique avec lequel il communique car il travaille avec un objet représentant un flux. L’abstraction est particulièrement importante et le programme manipule essentiellement des données qui entrent ou qui sortent, indépendamment de la nature physique du périphérique. 2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers La bibliothèque standard du C++ fournit plusieurs objets permettant de communiquer avec l’environnement du programme dont les connus sont : Ces objets sont déclarés dans l’en-tête avec et std::cin représente l’entrée standard. std::cout représente la sortie standard. std::cerr représente un flux destiné principalement aux messages d’erreur. La sortie standard avec std : :cout Nous avons déjà utilisé de nombreuses fois : std::cout << "Bonjour"; L’opérateur « envoie une donnée vers le flux situé à sa gauche, ici, la chaîne de caractères On peut enchaîner plusieurs opérations et chaque donnée est successivement envoyée vers le même std::cout << "Age : " << age << ’\n’; std::cout << "Age : " << age << ’\n’; peut donc être comprise comme une succession d’envois vers std::cout. L’entrée standard avec std : :cin Le flux std::cin fonctionne dans l’autre direction, es données sont extraites du flux et placées dans des variables du programme, par exemple L’opérateur » extrait une donnée du flux situé à sa gauche et on peut également lire plusieurs valeursAccueil2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers Si l’utilisateur saisit : la première valeur sera placée dans x et la seconde dans y. Une première difficulté : les espaces L’opérateur » est très pratique pour lire des nombres ou des mots isolés, mais il considère généralement les espaces comme des séparateurs, considérons par exemple : Si l’utilisateur écrit : sera placé dans nom. Pour lire une ligne complète, on peut utiliser : std::getline(std::cin, nom); Cette fonction lit les caractères jusqu’à la fin de la ligne. Le piège de std : :getline après std : :cin Considérons le programme suivant : std::cout << "Age : "; std::cout << "Nom : "; std::getline(std::cin, nom); On pourrait s’attendre à ce que le programme attende la saisie du nom, mais ce n’est généralement pas ce qui se produit. Lorsque l’utilisateur valide son âge avec la touche Entrée, le caractère de fin de ligne reste dans le flux d’entrée (l’opérateur » lit le nombre, mais laisse ce caractère dans le flux) et l’appel suivant à getline() rencontre immédiatement cette fin de ligne et considère donc qu’il vient de lire une ligne vide. Une solution consiste à éliminer les caractères restants jusqu’à la fin de la ligne : std::numeric_limits<std::streamsize>::max(),Accueil2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers On peut ensuite utiliser : std::getline(std::cin, nom); Ce comportement montre qu’un flux possède un état et contient une succession de caractères qui sont consommés progressivement par les opérations de lecture. std : :endl et le caractère \n Pour terminer une ligne, on rencontre souvent : std::cout << std::endl; std::cout << ’\n’; Ces deux écritures ne sont pas exactement équivalentes. ajoute simplement un saut de ligne. tandis que std::endl ajoute également un saut de ligne, mais demande en plus au flux de vider son tampon. Cette opération est appelée un flush. Dans de nombreux cas, ce vidage immédiat n’est pas nécessaire et on préférera donc généralement : pour les simples changements de ligne. Les flux utilisent des tampons Lorsqu’un programme écrit : std::cout << valeur; les caractères produits ne sont pas nécessairement envoyés immédiatement au périphérique mais peuvent être placés temporairement dans une zone mémoire appelée tampon, ou buffer et le contenu du tampon est envoyé ultérieurement. Cette technique améliore les performances : écrire un grand bloc de données est généralement plus efficace que d’effectuer une opération physique pour chaque caractère. Le vidage du tampon peut être demandé explicitement avec : ou indirectement avec : Dans la majorité des affichages ordinaires, il n’est pas nécessaire de forcer ce vidage.Accueil2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers Le flux d’erreur std : :cerr Un programme peut distinguer ses résultats normaux de ses messages d’erreur. std::cout << "Calcul termine.\n"; std::cerr << "Erreur : fichier introuvable.\n"; Les deux messages peuvent apparaître dans le même terminal, mais ils sont envoyés vers deux flux différents. Cette distinction devient importante lorsqu’on redirige les sorties du programme ou lorsqu’un autre programme doit récupérer ses résultats. Les données normales et les messages d’erreur peuvent alors être traités séparément. Le formatage des nombres Les flux permettent également de contrôler la présentation des valeurs. Les outils principaux de contrôle se trouvent dans : double x = 3.141592653589793; std::cout << x << ’\n’; Le nombre de chiffres affichés peut être contrôlé avec std::setprecision(). std::cout << std::setprecision(10) la précision correspond au nombre de chiffres après la virgule : std::cout << std::fixed << std::setprecision(4) La notation scientifique peut être demandée avec : Ces outils seront particulièrement utiles lorsque nous enregistrerons des résultats numériques dansAccueil2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers Pourquoi utiliser des fichiers ? Les données stockées dans les variables disparaissent normalement lorsque le programme se termine donc pour conserver des résultats, il faut les enregistrer sur un support persistant, un fichier permet par exemple, qui permet d’enregidtrer • les paramètres d’une expérience ; • les résultats d’un calcul ; • une série de mesures ; • l’évolution d’une simulation ; • les coordonnées de points destinés à un graphique. Dans une simulation, on pourrait par exemple enregistrer : La première colonne pourrait représenter le temps et la seconde la taille d’une population et un autre programme peut ensuite lire ces données ou les représenter graphiquement. Écrire dans un fichier avec std : :ofstream Pour écrire dans un fichier, la bibliothèque standard fournit std::ofstream et pour l’utiliser, il faut Un fichier peut être ouvert directement lors de la construction du flux : std::ofstream fichier("resultats.txt"); L’objet fichier est maintenant un flux de sortie et on peut donc utiliser le même opérateur que fichier << "Bonjour\n"; fichier << x << ’\n’; La similitude avec std::cout n’est pas accidentelle, dans les deux cas, nous écrivons dans un flux Vérifier l’ouverture du fichier L’ouverture d’un fichier peut échouer car le chemin peut être incorrect, le répertoire peut ne pas exister ou le programme peut ne pas disposer des autorisations nécessaires. Il faut donc pouvoir vérifier que le flux est utilisable, par exempleAccueil2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers std::ofstream fichier("resultats.txt"); std::cerr << "Impossible d’ouvrir le fichier.\n"; L’objet flux peut être utilisé directement comme condition et si son état indique une erreur, la Écrire une série de données Considérons deux valeurs calculées dans une boucle, on peut écrire : std::ofstream fichier("resultats.txt"); std::cerr << "Impossible d’ouvrir le fichier.\n"; for (int i = 0; i < 10; ++i) fichier << x << ’ ’ << y << ’\n’; Le fichier obtenu pourrait contenir : Ce format très simple est déjà suffisant pour de nombreux programmes scientifiques qui sera directement exploitable par gnuplot. Faut-il fermer explicitement le fichier ? Un fichier ouvert avec std::ofstream peut être fermé avec Cette opération n’est généralement pas nécessaire lorsque le flux arrive simplement à la fin de sa durée de vie, considérons :Accueil2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers std::ofstream fichier("resultats.txt"); fichier << "Resultats\n"; À la fin de la fonction, l’objet fichier est détruit car son destructeur ferme automatiquement le fichier et nous retrouvons ici exactement le principe étudié avec le RAII : la durée de vie de la ressource est liée à celle de l’objet. Le fichier est une ressource, tout comme une zone mémoire dynamiqu, le RAII ne concerne donc pas uniquement la mémoire. Lire un fichier avec std::ifstream Pour lire un fichier, on utilise : std::ifstream fichier("donnees.txt"); std::cerr << "Impossible d’ouvrir le fichier.\n"; Supposons que le fichier contienne : On peut lire deux valeurs avec : La syntaxe est exactement la même qu’avec : La source des données a changé, mais le principe du flux reste le même. Lire tout un fichier On rencontre souvent une construction comme :Accueil2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers while (fichier >> x >> y) std::cout << x << ’ ’ << y << ’\n’; Cette écriture est particulièrement importante et l’expression : effectue la lecture puis permet de tester l’état du flux et tant que les deux valeurs peuvent être lues correctement, la boucle continue. Lorsque la lecture échoue, la condition devient fausse. Pourquoi ne pas utiliser while ( !fichier.eof()) ? Une construction tentante est : while (!fichier.eof()) Cette méthode est déconseillée, la raison est subtile mais importante : la fin du fichier n’est détectée qu’après une tentative de lecture qui échoue. Lorsque la boucle teste eof(), elle peut donc entrer une fois de trop dans son corps, donc, la bonne méthode consiste à tester directement l’opération de lecture : while (fichier >> x >> y) Le traitement n’est alors exécuté que lorsque les données ont réellement été lues. Lire un fichier ligne par ligne Lorsqu’un fichier contient du texte, il peut être préférable de le lire ligne par ligne en utilisant à std::ifstream fichier("texte.txt"); while (std::getline(fichier, ligne)) std::cout << ligne << ’\n’;Accueil2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers La même fonction peut donc lire une ligne depuis : Encore une fois, le mécanisme repose sur l’abstraction du flux. Remplacer ou compléter un fichier std::ofstream fichier("resultats.txt"); ouvre généralement le fichier pour l’écriture en remplaçant son ancien contenu. et si l’on souhaite ajouter de nouvelles données à la fin du fichier, on peut utiliser le mode : std::ofstream fichier( Les nouvelles données seront alors ajoutées après celles qui existent déjà. std::fstream - lecture et écriture peut être utilisée lorsqu’un même fichier doit être ouvert en lecture et en écriture, par exemple : std::fstream fichier( std::ios::in | std::ios::out Les modes sont combinés avec l’opérateur : On rencontre notamment : • std::ios::in pour la lecture ; • std::ios::out pour l’écriture ; • std::ios::app pour ajouter à la fin ; • std::ios::trunc pour supprimer le contenu existant ; • std::ios::binary pour ouvrir en mode binaire. Pour de nombreux programmes simples, ifstream et ofstream restent cependant plus explicites.Accueil2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers L’état d’un flux Un flux conserve des informations sur le résultat des opérations effectuées, on peut notamment • une opération réussie ; • une fin de fichier ; • une erreur de format ; • une erreur plus grave empêchant le fonctionnement du flux. Plusieurs fonctions permettent d’interroger cet état : Dans la plupart des lectures ordinaires, il est cependant préférable de tester directement l’opération : if (fichier >> valeur) while (fichier >> valeur) Cette écriture associe directement le traitement à la réussite de la lecture. Une erreur de format Supposons que le programme attende un entier : mais que l’utilisateur saisisse : La conversion échoue et le flux entre alors dans un état d’erreur et par conséquent, les lectures suivantes échoueront également tant que cet état n’aura pas été corrigé. On peut réinitialiser les indicateurs d’erreur avec : puis éliminer les caractères incorrects avec, par exemple : std::numeric_limits<std::streamsize>::max(), Cette distinction est importante car clear() efface l’état d’erreur du flux, mais ne supprime pas les caractères qui ont provoqué l’erreur tandis que ignore() permet d’éliminer les caractères indésirables.Accueil2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers Les chemins de fichiers std::ofstream fichier("resultats.txt"); utilise un chemin relatif c.-à-d. que le fichier est recherché ou créé relativement au répertoire de travail du programme. Ce répertoire n’est pas nécessairement celui dans lequel se trouve le fichier source C++ et cette distinction est particulièrement importante lorsqu’un programme est lancé depuis un IDE. On peut également utiliser un chemin absolu, mais celui-ci dépend fortement de l’organisation de l’ordinateur sur lequel le programme est exécuté. Pour les programmes destinés à être déplacés d’une machine à une autre, les chemins relatifs sont souvent préférables lorsque l’organisation du projet le Considérons un programme qui calcule les valeurs de la fonction : pour différentes valeurs de x et les enregistre dans un fichier. std::ofstream fichier("resultats.txt"); << "Impossible d’ouvrir le fichier.\n"; for (int i = 0; i <= 100; ++i) Le programme ne produit pas directement de graphique mais ll produit des données. Cette séparation est extrêmement utile :Accueil2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers calcul −→ fichier de données Le fichier peut ensuite être utilisé par un autre outil et dans le chapitre suivant, nous utiliserons précisément cette méthode avec gnuplot : programme C++ −→ fichier de données −→ gnuplot −→ graphique Une même abstraction pour plusieurs destinations Nous pouvons maintenant comparer : Dans le premier cas, la destination est la sortie standard, dans le second, la destination est un fichier mais l’opération fondamentale est la même : envoyer une valeur vers un flux de sortie. expriment toutes deux l’extraction d’une valeur depuis un flux d’entrée et c’est cette abstraction qui donne sa cohérence au système d’entrées/sorties du C++.Accueil2026 - C++ Partie III Les entrées et sorties : flux, console et fichiers • Les entrées et sorties du C++ reposent principalement sur la notion de flux. • Un flux d’entrée fournit des données au programme, tandis qu’un flux de sortie reçoit les données produites par celui-ci. • std::cin, std::cout et std::cerr représentent les principaux flux standards. • Les classes std::ifstream, std::ofstream et std::fstream appliquent la même abstraction aux fichiers. • Les opérateurs » et « permettent donc d’utiliser une syntaxe similaire avec la console et • Les flux possèdent un état : une lecture peut réussir, atteindre la fin d’un fichier ou échouer à cause d’une donnée incorrecte. • Lorsqu’on lit successivement des données, il est généralement préférable de tester directement l’opération de lecture : while (fichier >> valeur) plutôt que de tenter de prévoir la fin du fichier avec eof(). • Enfin, les flux de fichiers illustrent à nouveau le principe RAII : l’objet représentant le flux possède la ressource et sa destruction entraîne automatiquement la fermeture du fichier les entrées/sorties constituent ainsi le lien entre les calculs effectués en mémoire et le monde extérieur. • Les E/S permettront notamment de conserver les résultats d’une simulation et de les transmettre à un logiciel de représentation graphique tel que gnuplot.AccueilP3-Chap-03- Les entrées et sorties : flux, console et fichiersAccueilTermes à ajouter au glossaire