Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.
lire_et_modifier_des_fichiers [2014/04/22 12:20] gbdivers |
lire_et_modifier_des_fichiers [2020/10/06 19:10] (Version actuelle) gbdivers |
||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
- | > **Ce cours est une mise à jour du cours C++ de OpenClassRoom pour le mettre à jour pour le C++11/14. Le cours d'origine est consultable sur la page suivante : [[http://fr.openclassrooms.com/informatique/cours/programmez-avec-le-langage-c|Programmez avec le langage C++]], par Mathieu Nebra et Matthieu Schaller. Ce cours est sous licence CC-BY-NC-SA.** | ||
- | ^ [[les_tableaux|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[tp_-_le_mot_mystere|Chapitre suivant]] ^ | ||
- | __ parler de put et write ? __ | + | <note warning>Ce cours n'est plus à jour, il est préférable de ne pas le suivre. Je vous recommande le cours sur Zeste de Savoir : https://zestedesavoir.com/tutoriels/822/la-programmation-en-c-moderne/.</note> |
- | __ tester si ouvert avec is_open, eof, bad, fail ? __ | ||
- | __ formating : precision, width, flags, etc __ | ||
- | |||
- | ====== Lire et modifier 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'entraînement, vous seriez capables de créer de vraies applications. Vous commencez à connaître les bases du C++, mais 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 avait saisi. 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 des fichiers et écrire dedans. Et même dans le monde des jeux vidéo, on a besoin de cela : il y a bien sûr les fichiers de sauvegardes, 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. | ||
- | |||
- | ===== Écrire dans un fichier ===== | ||
- | |||
- | La première chose à faire quand on veut manipuler des fichiers, c'est de les ouvrir. Eh 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 modifier 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. | ||
- | |||
- | <note info>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.</note> | ||
- | |||
- | 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 dans 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, voyez cela comme de grosses variables améliorées. Ces objets contiennent beaucoup d'informations sur les fichiers ouverts et proposent des fonctionnalités comme fermer le fichier, retourner au début et bien d'autres encore. (pourquoi parler d'objet maintenant alors qu'on en parle pas avant, quand on utilise string et vector ?) __ | ||
- | |||
- | __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'' et dont la valeur serait le chemin d'accès du fichier à lire.__ | ||
- | |||
- | __Comme pour les variables, il y a quelques règles à suivre pour le choix du nom du flux:__ | ||
- | |||
- | * __les noms des flux sont constitués de lettres, de chiffres et du tiret-bas _ uniquement ;__ | ||
- | * __le premier caractère doit être une lettre (majuscule ou minuscule) ;__ | ||
- | * __on ne peut pas utiliser d'accents ;__ | ||
- | * __on ne peut pas utiliser d'espaces dans le nom.__ | ||
- | |||
- | ''Vous l'aurez remarqué, ce sont exactement les mêmes règles que pour les variables. Je ne vous ferai donc pas l'offense de répéter les règles que nous utilisons dans ce cours et qui sont très souvent adoptées par les programmeurs. Tout a déjà été dit au chapitre 4. Dans la suite de ce chapitre nous utiliserons le nom monFlux comme nom pour les exemples de flux. Il satisfait tous les critères; j'espère que vous en conviendrez.'' | ||
- | |||
- | __ Un flux est une variable, pourquoi réexpliquer les variables ? Cela pourrait faire penser que le flux est différent __ | ||
- | |||
- | <code cpp main.cpp> | ||
- | #include <iostream> | ||
- | #include <fstream> | ||
- | using namespace std; | ||
- | |||
- | int main() | ||
- | { | ||
- | ofstream monFlux { R"(C:\Nanoc\scores.txt)" }; | ||
- | // Déclaration d'un flux permettant d'écrire dans le fichier | ||
- | // "C:\Nanoc\scores.txt" | ||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | J'ai indiqué entre guillemets le chemin d'accès au fichier. Ce chemin doit prendre l'une ou l'autre des deux formes suivantes : | ||
- | |||
- | * Un chemin absolu, c'est-à-dire montrant l'emplacement du fichier depuis la racine du disque. Par exemple : ''C:\Nanoc\C++/Fichiers\scores.txt''. | ||
- | * Un chemin relatif, c'est-à-dire montrant 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++\''. | ||
- | |||
- | À partir de là, on peut utiliser le flux pour écrire dans le fichier. | ||
- | |||
- | <note info>Si le fichier n'existait pas, le programme le créerait automatiquement ! Par contre, il faut que le dossier existe. Dans l'exemple précédent, le dossier ''C:\Nanoc\C++\Fichiers'' doit exister. Si ce n'est pas le cas, rien ne sera écrit.</note> | ||
- | |||
- | __Le plus souvent, le nom du fichier est contenu dans une chaîne de caractères string.__ | ||
- | |||
- | <code cpp> | ||
- | string const nomFichier { R"(C:\Nanoc\scores.txt)" }; | ||
- | |||
- | ofstream monFlux { nomFichier }; | ||
- | // Déclaration d'un flux permettant d'écrire dans un fichier. | ||
- | </code> | ||
- | |||
- | 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 cela qu'il faut toujours tester si tout s'est bien passé. On utilise pour cela 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. | ||
- | |||
- | <code cpp> | ||
- | ofstream monFlux { R"(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; | ||
- | } | ||
- | </code> | ||
- | |||
- | __ Utiliser des exceptions ici ? __ | ||
- | |||
- | Tout est donc prêt pour l'écriture. Et vous allez voir que ce n'est pas vraiment nouveau. | ||
- | |||
- | ==== É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 : ce sont les chevrons ''<<'' qu'il faut utiliser. | ||
- | |||
- | <code cpp main.cpp> | ||
- | #include <iostream> | ||
- | #include <fstream> | ||
- | #include <string> | ||
- | using namespace std; | ||
- | |||
- | int main() | ||
- | { | ||
- | string const nomFichier { R"(C:\Nanoc\scores.txt)" }; | ||
- | ofstream monFlux { nomFichier }; | ||
- | |||
- | 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; | ||
- | } | ||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | Si j'exécute ce programme, je retrouve ensuite sur mon disque un fichier ''scores.txt'' dont le contenu est présenté en figure suivante. | ||
- | |||
- | {{ http://uploads.siteduzero.com/files/289001_290000/289410.png }} | ||
- | <note legende>Le fichier une fois qu'il a été écrit</note> | ||
- | |||
- | Essayez par vous-mêmes ! | ||
- | Vous pouvez par exemple écrire un programme qui demande à l'utilisateur son nom et son âge 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. | ||
- | |||
- | <note question>Que se passe-t-il si le fichier existe déjà ?</note> | ||
- | |||
- | Il sera supprimé et remplacé par ce que vous écrivez, ce qui est problématique 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 à la création du flux : | ||
- | |||
- | <code cpp> | ||
- | ofstream monFlux { R"(C:\Nanoc\scores.txt)", ios::app }; | ||
- | </code> | ||
- | |||
- | <note info>''std::app'' est un raccourci pour //append//, le verbe anglais qui signifie « ajouter à la fin ».</note> | ||
- | |||
- | Avec cela, plus de problème d'écrasement des données : tout ce qui sera écrit sera ajouté à la fin. | ||
- | |||
- | ==== Exercices ==== | ||
- | |||
- | === Créer un fichier à partir du texte saisie par l'utilisateur === | ||
- | |||
- | **Exercice 1** : Créez un programme qui demande à l'utilisateur d'entrer un nom de fichier et du texte quelconque et qui enregistre ce texte dans un fichier en utilisant le nom donné. | ||
- | |||
- | **Exercice 2** : Améliorez le programme précédant en ajoutant une boucle qui permet d'entrer plusieurs textes quelconques. Les textes saisies par l'utilisateur sont enregistrés directement dans le fichier. Lorsque l'utilisateur entre une ligne vide, le programme ferme le fichier et se termine. | ||
- | |||
- | **Exercice 3** : Modifiez le programme précédent pour que les textes saisies par l'utilisateur soient enregistrés dans le programme et enregistrer en une seule fois, lorsque l'application se termine. | ||
- | |||
- | === Créer un fichier de tableur === | ||
- | |||
- | Il est possible de créer fichier pour un tableur comme Excel ou LibreOffice Calc en créant un fichier texte avec l'extensions ''.xls'' et en séparant chaque cellule par une tabulation '\t'. Ainsi, le fichier suivant : | ||
- | |||
- | <code cpp data.xls> | ||
- | 1 2 3 | ||
- | 4 5 6 | ||
- | 7 8 9 | ||
- | </code> | ||
- | |||
- | sera ouvert dans LibreOffice Calc : | ||
- | |||
- | {{ :data_libreoffice.png |}} | ||
- | |||
- | **Exercice 1** : Créer un programme qui génère ce fichier, sans utiliser de boucle. | ||
- | |||
- | **Exercice 2** : Créer un programme qui génère ce fichier, en utilisant une boucle. | ||
- | |||
- | **Exercice 3** : Créer une programme qui demande à l'utilisateur d'entrer les dimensions du tableau à créer, puis les textes à insérer dans les cellules. | ||
- | |||
- | **Exercice 4** : Créer un programme qui permet à l'utilisateur d'entrer les valeurs à insérer dans les cellules du tableau. Si l'utilisateur entre une ligne vide, cela veut dire qu'il passe à la ligne suivante. S'il entre deux lignes vides, cela veut dire que la création du tableau est terminée. | ||
- | |||
- | ===== Lire un fichier ===== | ||
- | |||
- | Nous avons appris à écrire dans un fichier, voyons maintenant comment fonctionne la lecture d'un 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, afin d'éviter les erreurs. | ||
- | |||
- | <code cpp> | ||
- | ifstream monFlux { R"(C:\Nanoc\C++\data.txt)" }; // Ouverture d'un fichier en lecture | ||
- | |||
- | if(monFlux) | ||
- | { | ||
- | // Tout est prêt pour la lecture. | ||
- | } | ||
- | else | ||
- | { | ||
- | cout << "ERREUR: Impossible d'ouvrir le fichier en lecture." << endl; | ||
- | } | ||
- | </code> | ||
- | |||
- | 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 méthodes 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. | ||
- | |||
- | <code cpp> | ||
- | string ligne {}; | ||
- | getline(monFlux, ligne); // On lit une ligne complète | ||
- | </code> | ||
- | |||
- | 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.__ | ||
- | |||
- | <code cpp> | ||
- | double nombre {}; | ||
- | monFlux >> nombre; // Lit un nombre à virgule depuis le fichier | ||
- | string mot {}; | ||
- | monFlux >> mot; // Lit un mot depuis le fichier | ||
- | </code> | ||
- | |||
- | Cette méthode lit ce qui se trouve entre l'endroit où l'on se situe dans le fichier et l'espace suivant. 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 elle est tout aussi simple, je vous rassure. | ||
- | |||
- | <code cpp> | ||
- | char a {}; | ||
- | monFlux.get(a); | ||
- | </code> | ||
- | |||
- | Ce code lit une seule lettre et la stocke dans la variable ''a''. | ||
- | |||
- | <note info>Cette méthode lit réellement tous les caractères. Les espaces '' '', retours à la ligne ''\n'' et tabulations ''\t'' sont, entre autres, lus par cette fonction. __Bien que bizarres__, ces caractères seront néanmoins stockés dans la variable.</note> | ||
- | |||
- | Souvenez-vous de ce que nous avions vu au __chapitre 5__ lorsque nous avons découvert l'utilisation de ''cin''. Nous avions appris qu'il fallait utiliser ''cin.ignore()'' lorsque l'on passait de la lecture mot par mot à la lecture ligne par ligne. Il en va de même ici. Il faudra donc écrire : | ||
- | |||
- | <code cpp> | ||
- | ifstream monFlux { R"(C:\Nanoc\C++\data.txt)" }; | ||
- | |||
- | string mot {}; | ||
- | monFlux >> mot; // On lit un mot depuis le fichier | ||
- | |||
- | monFlux.ignore(); // On change de mode | ||
- | |||
- | string ligne {}; | ||
- | getline(monFlux, ligne); // On lit une ligne complète | ||
- | </code> | ||
- | |||
- | __Mais je vous avouerai que ce n'est pas souvent que l'on change de mode de lecture en cours de route.__ | ||
- | |||
- | ==== 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 la valeur renvoyée par la fonction ''getline()''. En effet, en plus de lire une ligne, cette fonction renvoie un ''bool'' indiquant si l'on peut continuer à lire. Si la fonction renvoie ''true'', tout va bien, la lecture peut continuer. Si elle renvoie ''false'', c'est qu'on est arrivé à la fin du fichier ou qu'il y a eu une erreur. Dans les deux cas, il faut s'arrêter de lire. | ||
- | |||
- | 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 : | ||
- | |||
- | <code cpp main.cpp> | ||
- | #include <iostream> | ||
- | #include <fstream> | ||
- | #include <string> | ||
- | using namespace std; | ||
- | |||
- | int main() | ||
- | { | ||
- | ifstream fichier { R"(C:\Nanoc\C++\data.txt)" }; | ||
- | |||
- | if(fichier) | ||
- | { | ||
- | // L'ouverture s'est bien passée, on peut donc lire | ||
- | |||
- | string ligne {}; // Une variable pour stocker les lignes lues | ||
- | |||
- | while(getline(fichier, ligne)) // Tant qu'on n'est pas à la fin, on lit | ||
- | { | ||
- | cout << ligne << endl; | ||
- | // Et on l'affiche dans la console | ||
- | // Ou alors on fait quelque chose avec cette ligne | ||
- | // À vous de voir | ||
- | } | ||
- | } | ||
- | else | ||
- | { | ||
- | cout << "ERREUR: Impossible d'ouvrir le fichier en lecture." << endl; | ||
- | } | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | Une fois que l'on a lu les lignes, on peut les manipuler facilement. Ici, je me contente d'afficher les lignes mais, dans un programme réel on les utiliserait autrement. La seule limite est votre imagination. | ||
- | C'est la méthode la plus utilisée pour lire un fichier. Une fois que l'on a récupéré les lignes dans une variable string, on peut facilement travailler dessus grâce aux fonctions utilisables sur les chaînes de caractères. | ||
- | |||
- | ==== Exercices ==== | ||
- | |||
- | === Lire un fichier de tableur === | ||
- | |||
- | **Exercice 1** : Créer un programme qui lit un fichier de tableur et l'affiche. | ||
- | |||
- | ===== Quelques astuces ===== | ||
- | |||
- | Il ne reste que quelques astuces à voir et vous saurez alors tout ce qu'il faut sur les fichiers. | ||
- | |||
- | ==== Fermer prématurément un fichier ==== | ||
- | |||
- | Je vous ai expliqué en tout début de chapitre comment ouvrir un fichier. Mais je ne vous ai pas montré comment le refermer. Ce n'est pas un oubli de ma part, il s'avère juste que ce n'est pas nécessaire. Les fichiers ouverts sont automatiquement refermés lorsque l'on sort du bloc où le flux est déclaré. | ||
- | |||
- | <code cpp> | ||
- | void f() | ||
- | { | ||
- | ofstream flux { R"(C:\Nanoc\C++\data.txt)" }; // Le fichier est ouvert | ||
- | |||
- | // Utilisation du fichier | ||
- | |||
- | } // Lorsque l'on sort du bloc, le fichier est automatiquement refermé | ||
- | </code> | ||
- | |||
- | Il n'y a donc rien à faire. Aucun risque d'oublier de refermer le fichier ouvert. | ||
- | |||
- | Il arrive par contre qu'on ait besoin de fermer le fichier avant sa fermeture automatique. Il faut alors utiliser la fonction ''close()'' des flux. | ||
- | |||
- | <code cpp> | ||
- | void f() | ||
- | { | ||
- | ofstream flux { R"(C:\Nanoc\C++\data.txt)" }; // Le fichier est ouvert | ||
- | |||
- | // Utilisation du fichier | ||
- | |||
- | flux.close(); // On referme le fichier | ||
- | // On ne peut plus écrire dans le fichier à partir d'ici | ||
- | } | ||
- | </code> | ||
- | |||
- | De la même manière, il est possible de retarder l'ouverture d'un fichier après la déclaration du flux en utilisant la fonction ''open()''. | ||
- | |||
- | <code cpp> | ||
- | void f() | ||
- | { | ||
- | ofstream flux {}; // Un flux sans fichier associé | ||
- | |||
- | flux.open { R"(C:\Nanoc\C++\data.txt)" }; // On ouvre le fichier C:/Nanoc/data.txt | ||
- | |||
- | // Utilisation du fichier | ||
- | |||
- | flux.close(); // On referme le fichier | ||
- | // On ne peut plus écrire dans le fichier à partir d'ici | ||
- | } | ||
- | </code> | ||
- | |||
- | Comme vous le voyez, c'est très simple. Toutefois, dans la majorité des cas, c'est inutile. Ouvrir directement le fichier et le laisser se fermer automatiquement suffit. | ||
- | |||
- | ==== Le curseur dans le fichier ==== | ||
- | |||
- | Plongeons un petit peu plus dans les détails techniques et 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. Dans la figure suivante, le curseur se situe après les deux « s » sur la quatrième ligne. | ||
- | |||
- | {{ http://uploads.siteduzero.com/files/290001_291000/290290.png }} | ||
- | <note legende>Position du curseur</note> | ||
- | |||
- | 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 cela. 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 : | ||
- | |||
- | <code cpp> | ||
- | ifstream fichier { R"(C:\Nanoc\C++\data.txt)" }; | ||
- | </code> | ||
- | |||
- | 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. Ce faisant, le « curseur C++ » se déplace jusqu'au début du mot suivant, comme à la figure suivante. | ||
- | |||
- | {{ http://uploads.siteduzero.com/files/290001_291000/290293.png }} | ||
- | <note legende>Le curseur a été déplacé</note> | ||
- | |||
- | Le mot suivant qui peut être lu est donc « : », puis « 118218 », et ainsi de suite jusqu'à la fin. On est donc obligé 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 caractères ». On peut ainsi lire uniquement 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. | ||
- | |||
- | ==== 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 entrant et sortant et, en plus, ce sont des noms bizarres.__ Je vous ai mis les noms des deux fonctions dans un petit tableau | ||
- | |||
- | ^ Pour ifstream ^ Pour ofstream ^ | ||
- | | tellg() | tellp() | | ||
- | |||
- | En revanche, elles s'utilisent toutes les deux de la même manière. | ||
- | |||
- | <code cpp> | ||
- | ofstream fichier { R"(C:\Nanoc\C++\data.txt)" }; | ||
- | |||
- | int const position = fichier.tellp(); // On récupére la position | ||
- | |||
- | cout << "Nous nous situons au " << position << "eme caractere du fichier." << endl; | ||
- | </code> | ||
- | |||
- | ==== Se déplacer ==== | ||
- | |||
- | Là encore, il existe deux fonctions, une pour chaque type de flux. | ||
- | |||
- | ^ Pour ifstream ^ Pour ofstream ^ | ||
- | | seekg() | seekp() | | ||
- | |||
- | Elles s'utilisent 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 : | ||
- | |||
- | <code cpp> | ||
- | flux.seekp(nombreCaracteres, position); | ||
- | </code> | ||
- | |||
- | 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ù se situe le curseur, 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 ? | ||
- | Bon, je vous montre. | ||
- | |||
- | <code cpp main.cpp> | ||
- | #include <iostream> | ||
- | #include <fstream> | ||
- | using namespace std; | ||
- | |||
- | int main() | ||
- | { | ||
- | ifstream fichier { R"(C:\Nanoc\meilleursScores.txt)" }; // On ouvre le fichier | ||
- | fichier.seekg(0, ios::end); // On se déplace à la fin du fichier | ||
- | |||
- | int const taille = fichier.tellg(); | ||
- | // On récupère la position qui correspond donc a la taille du fichier ! | ||
- | |||
- | cout << "Taille du fichier : " << taille << " octets." << endl; | ||
- | |||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | Je suis sûr que vous le saviez ! | ||
- | |||
- | Voilà, on a fait le tour des notions principales. Vous êtes prêts à vous lancer seuls dans le vaste monde des fichiers. | ||
- | |||
- | ==== Exercices ==== | ||
- | |||
- | ===== En résumé ===== | ||
- | |||
- | * En C++, pour lire ou écrire dans un fichier, on doit inclure le fichier d'en-tête ''<fstream>''. | ||
- | * On doit créer un objet de type ''ofstream'' pour ouvrir un fichier en écriture et ''ifstream'' pour l'ouvrir en lecture. | ||
- | * L'écriture se fait comme avec ''cout'' : ''monFlux << "Texte";'' tandis que la lecture se fait comme avec ''cin'' : ''monFlux >> variable;''. | ||
- | * On peut lire un fichier ligne par ligne avec ''getline()''. | ||
- | * Le curseur indique à quelle position vous êtes au sein du fichier, pendant une opération de lecture ou d'écriture. Au besoin, il est possible de déplacer ce curseur. | ||
- | |||
- | ^ [[les_tableaux|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[tp_-_le_mot_mystere|Chapitre suivant]] ^ | ||
- | |||
- | ===== Travaux pratiques ===== | ||
- | |||
- | ==== Convertisseur de formats de tableur ==== | ||
- | |||
- | Faire les exercices "Créer un fichier de tableur". | ||
- | |||
- | En fait, il existe plusieurs formats de fichier de tableur possible. Ils se distinguent selon différents paramètres : | ||
- | * le caractère utilisé pour la séparation des cellules : tabulation '\t', espace ' ', virgule, point-virgule, etc. | ||
- | * ajouter des guillemets pour encadrer le texte. | ||
- | * utiliser le point ou la virgule comme séparateur décimal. | ||
- | * autre ? | ||
- | |||
- | Le but de ce travail pratique va être de créer un programme qui prend en entrée un fichier dans un certain format et va créer un nouveau fichier avec un nouveau format. Les options de format pour le fichier lu et le fichier écrit sera précisé dans les options du programme. Par exemple : | ||
- | |||
- | <code> | ||
- | tp-convertiseur -in input.xls -in-separator tabulation -in-decimal point -out output.xls -out-separator virgule -out-decimal point | ||
- | </code> | ||
- | |||
- | Pour ouvrir un fichier "input.xls", utilisant des tabulations et des points, et l'enregistre sous le nom "output.xls", au format virgule et point. | ||
- | |||
- | ==== Rechercher et remplacer dans un fichier ==== | ||
- | |||
- | Créer un programme qui prend une chaîne de caractères et faire un rechercher-remplacer dans un fichier. | ||
- | |||
- | Utilisez les expressions régulières ? | ||
- | |||
- | ===== Pour aller plus loin ===== | ||
- | |||
- | * La documentation de [[http://en.cppreference.com/w/cpp/io/basic_fstream|fstream]], [[http://en.cppreference.com/w/cpp/io/basic_ofstream|ofstream]] et [[http://en.cppreference.com/w/cpp/io/basic_ifstream|ifstream]]. | ||
- | * La documentation de [[http://en.cppreference.com/w/cpp/io/basic_ostream/seekp|seekp]], [[http://en.cppreference.com/w/cpp/io/basic_istream/seekg|seekg]], [[http://en.cppreference.com/w/cpp/io/basic_ostream/tellp|tellp]] et [[http://en.cppreference.com/w/cpp/io/basic_istream/tellg|tellg]]. | ||
- | * [[http://fr.openclassrooms.com/informatique/cours/lecture-et-ecriture-dans-les-fichiers-en-c|Lecture et écriture dans les fichiers en C++]] | ||
- | |||
- | {{tag> Cours C++}} |