| ||
auteurs : HRS, Aurélien Regat-Barrel | ||
Le C++ standard ne permet pas de tester de manière fiable qu'un fichier existe (celui-ci peut exister sans qu'il soit possible de le lire). Une solution portable consiste à utiliser la fonction exists de la bibliothèque boost::filesystem.
Il est en revanche possible de tester si un fichier existe et est accessible en lecture ou non, ce qui est suffisant dans la plupart des cas. Il est cependant important de noter qu'il est possible que le fichier soir créé / effacé entre le moment où l'on teste son accès et le moment où l'on crée / accède au fichier.
L'appel à fichier.fail() permet de tester si l'ouverture du flux s'est bien déroulée, ou autrement dit, si le fichier
est accessible en lecture. On aurait également pu écrire return fichier, qui fait appel à la conversion implicite en void*, et qui n'est rien d'autre qu'une syntaxe allegée pour fichier.fail(). Pour plus d'informations à ce sujet, consultez Comment fonctionnent les tests d'ouverture de fichier if ( fichier ) et if ( !fichier ) ?. |
| ||
auteur : Aurélien Regat-Barrel | ||
En cas d'échec de lecture / écriture, des indicateurs d'erreurs du flux sont positionnés et il suffit alors de tester l'état de ce dernier de la même manière que dans le cas du test de saisie avec cin ou du test d'ouverture de fichier.
|
| ||
auteur : Aurélien Regat-Barrel | ||
La valeur par défaut du mode d'ouverture du fichier dans le constructeur de std::ofstream est ios_base::out combiné à ios_base::trunc (pour truncate = tronquer). Ce dernier a pour conséquence d'écraser le contenu original du fichier ouvert. Pour éviter cela, il suffit de spécifier un autre mode d'ouverture, par exemple ios_base::app (pour append = ajouter à la suite):
|
| ||||
auteur : Aurélien Regat-Barrel | ||||
Si une lecture échoue parce que la fin de fichier a été atteinte, alors les indicateurs d'erreur du flux sont positionnés et il est possible de détecter la fin de fichier simplement en testant la réussite de la lecture. Pour avoir plus de précisions sur l'origine de l'échec, il est possible d'utiliser istream::eof() pour savoir si l'erreur est due à une fin de fichier, comme cela est fait dans la question Comment vérifier les valeurs saisies avec cin ?. Il convient cependant d'être prudent avec cette fonction car elle ne signale la fin de fichier qu'une fois qu'elle a été atteinte. Ainsi le code suivant est erroné:
Lorsque la dernière ligne sera lue, eof() renverra false si cette dernière ligne est terminée par un retour chariot car la lecture se sera arrêtée d'elle même sur ce caractère de fin de ligne (comportement de getline()) au lieu d'être arrêtée par la fin de fichier.
Ainsi, l'exemple précédent va compter une ligne en trop dans un tel cas. Voici maintenant le code corrigé :
Ce code fonctionne, mais une telle écriture est plus contraignante que le test direct de la réussite de lecture (qui de plus ne se limite pas à l'erreur fin de fichier). Aussi on préfère ne pas utiliser eof() et simplement écrire:
|
| ||
auteurs : dj.motte, Aurélien Regat-Barrel | ||
Il n'y a pas de fonction faisant partie du standard C++ qui permette d'obtenir la taille d'un fichier (sans l'ouvrir).
Il est cependant possible et facile d'y arriver en ouvrant le fichier, en se positionnant à sa fin, et en récupérant la nouvelle position courante.
|
| |||||
auteur : Aurélien Regat-Barrel | |||||
Pour que l'expression suivante compile :
le compilateur recherche un moyen de convertir l'objet std::ifstream en un booléen.
Ce dernier ne définit pas d'opérateur de conversion vers bool, mais en définit un pour void * :
Le but de cet opérateur est de permettre de caster un flux vers un pointeur afin justement d'indiquer un succès.
Ce dernier renvoie une valeur nulle si un des drapeaux d'erreur est positionné (failbit ou badbit).
Cette valeur nulle est à nouveau implicitement convertie en booléen par le compilateur.
Sa valeur est false en cas de pointeur nul, true pour toute autre valeur.
Les opérations implicites effectuées par le compilateur sont donc équivalentes à écrire soi-même :
L'expression
est elle un peu plus simple à comprendre. Il n'y a pas de conversion implicite, mais un simple appel à l'opérateur
qui renvoie directement un booléen qui vaut true si un des drapeaux d'erreur est positionné (failed state) et false si l'objet est dans un état valide (good state).
|
| ||
auteurs : LFE, Aurélien Regat-Barrel | ||
La classe std::istream dont hérite std::ifstream possède une fonction getline() mais cette dernière n'est pas pratique et est à utiliser avec précaution car elle demande un pointeur sur un tableau de char (char *) à remplir.
Aussi est-il préférable d'utiliser std::getline (déclaré dans <string>) qui permet d'extraire une ligne d'un istream sous la forme d'une string.
Il n'est donc plus nécessaire de se préoccuper de la taille de celle-ci.
Le petit programme ci-dessous lit un fichier ligne par ligne et l'affiche à l'écran.
std::getline peut aussi accepter un troisième paramètre qui permet de spécifier le délimiteur à utiliser comme marqueur de fin de ligne.
Si ce dernier n'est pas spécifié, le caractère '\n' est utilisé.
|
| ||
auteurs : ovh, Aurélien Regat-Barrel | ||
Pour charger l'intégralité d'un fichier en mémoire, il est judicieux d'utiliser un std::stringstream comme buffer ce qui permet de s'affranchir d'une allocation dynamique et de toute manipulation de pointeur.
La taille du buffer peut être différente de celle du fichier si le fichier n'a pas été ouvert en mode texte (en utilisant le mode std::ios_base::binary).
Il est aussi possible d'utiliser la fonction read de std::ifstream en ayant au préalable alloué un buffer suffisamment grand.
Pour connaître la taille à allouer, consulter la question Comment calculer la taille d'un fichier.
|
| ||
auteurs : Aurélien Regat-Barrel, Laurent Gomila | ||
Voici deux exemples qui illustrent comment arriver à ce résultat :
|
| ||
auteur : Aurélien Regat-Barrel | ||
Sous Windows, vous pouvez être surpris de lire un peu moins de caractères dans un fichier texte, ou d'en avoir écris plus que ce que vous avez demandé. Vous constatez cela en comparant la taille de votre flux (ou la taille théorique de ce que vous avez écris) avec la taille réelle du fichier indiquée par Windows.
Si votre programme n'est pas en cause, alors l'explication est simple : il s'agit du traitement particulier opéré par ofstream et ifstream lorsqu'ils travaillent sur des fichiers ouverts en mode texte, ce qui est le cas par défaut. Ceci n'a généralement pas d'influence, sauf sous DOS et Windows où un saut de ligne est matérialisé par la séquence des 2 caractères '\r' et '\n'. Pour assurer une meilleure portabilité du code, le langage C++ (ainsi que le langage C) considère qu'une fin de ligne est identifiée par un unique caractère '\n'. Pour cette raison, en mode texte sous Windows, l'objet ifstream va ignorer le caractère '\r' lorsqu'il rencontre une fin de ligne, et ofstream va insérer un '\r' avant chaque '\n' qu'on lui a demandé d'écrire. Ainsi, le fait que le fichier sur disque contiennent des caractère '\r' que vous n'avez pas lu / écris devrait expliquer votre différence de taille constatée. Pour lire ces caractères / ne pas générer leur écriture, il faut travailler en mode binaire en spécifiant le flag std::ios_base::binary.
Dans cet exemple, sous DOS/Windows, file_txt fait 9 octets, et file_bin en fait 6. |
| ||
auteur : Laurent Gomila | ||
Il est possible d'effectuer une lecture ou une écriture en mode texte dans un flux fstream, via les opérateurs >> et <<.
Mais il est également possible d'effectuer des lectures / écritures binaires via les fonctions membres read et write, un peu à la façon
de fread et fwrite.
Pour faciliter l'utilisation de ces fonctions, et notamment être certain de ne pas se tromper sur le second paramètre (la taille de la
donnée à lire / écrire), on peut construire des fonctions templates :
Il ne faut pas oublier que la gestion de données binaires n'est pas portable : selon les plateformes on aura des différences
d'endianess, ou de taille des types primitifs. Pour lire / ecrire des données de manière portable, concentrez-vous sur le mode texte si
vous le pouvez.
Si vous devez sérialiser des données plus complexes (gestion des données dynamiques, des conteneurs, des objets polymorphiques, etc),
il existe des mécanismes plus élaborés et plus efficaces, comme par exemple boost::serialization ou CArchive dans les MFC.
|
Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2008 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.