IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo
Sommaire > Entrées / sorties avec les flux > Manipulation des fichiers
        Comment tester l'existence d'un fichier ?
        Comment savoir si la lecture / écriture dans un fichier a réussi ?
        Comment écrire à la suite d'un fichier existant ?
        Comment détecter la fin de fichier lors de la lecture ?
        Comment calculer la taille d'un fichier ?
        Comment fonctionnent les tests d'ouverture de fichier if ( fichier ) et if ( !fichier ) ?
        Comment faire pour lire un fichier ligne par ligne ?
        Comment lire l'intégralité d'un fichier texte dans un buffer ?
        Comment compter le nombre de lignes d'un fichier ?
        Pourquoi n'ai-je pas le nombre de caractères attendus avec mon fichier ?
        Comment effectuer des lectures / écritures binaires dans un fichier ?



Comment tester l'existence d'un fichier ?
Mise à jour le 03/02/2007[haut]
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.
#include <fstream>
#include <string> 
#include <iostream>

bool is_readable( const std::string & file ) 
{ 
    std::ifstream fichier( file.c_str() ); 
    return !fichier.fail(); 
} 

void Exemple()
{
    using std::cout;
    if ( is_readable( "fichier.txt" ) ) 
    { 
        cout << "Fichier existant et lisible.\n"; 
    } 
    else 
    { 
        cout << "Fichier inexistant ou non lisible.\n"; 
    } 
}
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 ) ?.


Comment savoir si la lecture / écriture dans un fichier a réussi ?
Créé le 18/04/2005[haut]
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.
void test_lecture()
{
    ifstream file( "fichier.txt" );
    if ( !file )
    {
        cerr << "Erreur d'ouverture\n";
        return;
    }

    string line;
    if ( ! ( file >> line ) )
    {
        cerr << "Erreur de lecture\n";
        return;
    }
    cout << "Ligne lue : " << line;
}

void test_ecriture()
{
    ofstream file( "fichier.txt" );
    if ( !file )
    {
        cerr << "Erreur de création\n";
        return;
    }

    file << "Une ligne\n";
    if ( ! file )
    {
        cerr << "Erreur d'écriture\n";
        return;
    }
    cout << "L'écriture a réussi\n";
}

Comment écrire à la suite d'un fichier existant ?
Créé le 18/04/2005[haut]
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):
#include <fstream>

void AjouterUneLigne()
{
    // std::ios_base::out est automatiquement ajouté par le
    // constructeur de std::ofstream
    std::ofstream file( "fichier.txt", std::ios_base::app );
    file << "Une ligne\n";
}

int main()
{
    // création du fichier et écriture d'une ligne
    AjouterUneLigne();
    // ouverture du fichier existant et rajout d'une nouvelle ligne
    AjouterUneLigne();
    // "fichier.txt" contient 2 lignes
}

Comment détecter la fin de fichier lors de la lecture ?
Créé le 18/04/2005[haut]
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é:
Exemple erroné de détection de fin de fichier
ifstream file( "fichier.txt" );

// compter le nombre de lignes
int count = 0;
while ( ! file.eof() )
{
    string line;
    file >> line;
   ++count;
}
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é :
ifstream file( "fichier.txt" );

// compter le nombre de lignes
int count = 0;
while ( true )
{
    string line;
    getline( file, line );
    if ( file.eof() )
    {
         break;   
    }    
    ++count;
}
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 :
ifstream file( "fichier.txt" );

// compter le nombre de lignes
int count = 0;

string line;
while ( getline( file, line ) )
{
    ++count;
}

Comment calculer la taille d'un fichier ?
Mise à jour le 19/10/2004[haut]
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.
long GetFileSize( std::ifstream & Fichier )
{
    // sauvegarder la position courante
    long pos = Fichier.tellg();
    // se placer en fin de fichier
    Fichier.seekg( 0 , std::ios_base::end );
    // récupérer la nouvelle position = la taille du fichier
    long size = Fichier.tellg() ;
    // restaurer la position initiale du fichier
    Fichier.seekg( pos,  std::ios_base::beg ) ;
    return size ;
}

Comment fonctionnent les tests d'ouverture de fichier if ( fichier ) et if ( !fichier ) ?
Créé le 19/10/2004[haut]
auteur : Aurélien Regat-Barrel
Pour que l'expression suivante compile :
std::ifstream fichier( "fichier.txt" ); // ou n'importe quel objet dérivant de std::ios
if ( fichier ) // ce test échoue si le fichier n'est pas ouvert
{
    // OK
}
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 * :
operator void*( ) const;
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 :
std::ifstream fichier( "fichier.txt" ); // ou n'importe quel objet dérivant de std::ios
if ( fichier.operator void*() != 0 ) // ce test échoue si le fichier n'est pas ouvert
{
    // OK
}
L'expression
std::ifstream fichier( "fichier.txt" ); // ou n'importe quel objet dérivant de std::ios
if ( !fichier ) // ce test est vrai si l'ouverture échoue
{
    // ERREUR
}
est elle un peu plus simple à comprendre. Il n'y a pas de conversion implicite, mais un simple appel à l'opérateur
bool operator!() const;
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).


Comment faire pour lire un fichier ligne par ligne ?
Mise à jour le 19/10/2004[haut]
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.
#include <string>
#include <fstream>
#include <iostream>

int main()
{
    // le constructeur de ifstream permet d'ouvrir un fichier en lecture
    std::ifstream fichier( "fichier.txt" );

    if ( fichier ) // ce test échoue si le fichier n'est pas ouvert
    {
        std::string ligne; // variable contenant chaque ligne lue

        // cette boucle s'arrête dès qu'une erreur de lecture survient
        while ( std::getline( fichier, ligne ) )
        {
            // afficher la ligne à l'écran
            std::cout << ligne << std::endl;
        }
    }
}
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é.


Comment lire l'intégralité d'un fichier texte dans un buffer ?
Mise à jour le 19/10/2004[haut]
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.
#include <fstream>
#include <iostream>
#include <sstream>

int main()
{
    // le constructeur de ifstream permet d'ouvrir un fichier en lecture
    std::ifstream fichier( "fichier.txt" );

    if ( fichier ) // ce test échoue si le fichier n'est pas ouvert
    {
        std::stringstream buffer; // variable contenant l'intégralité du fichier
        // copier l'intégralité du fichier dans le buffer
        buffer << fichier.rdbuf();
        // nous n'avons plus besoin du fichier !
        fichier.close();
        // manipulations du buffer...
        std::cout << "Taille du buffer : " << buffer.str().size() << '\n';
    }
}
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.


Comment compter le nombre de lignes d'un fichier ?
Créé le 19/10/2004[haut]
auteurs : Aurélien Regat-Barrel, Laurent Gomila
Voici deux exemples qui illustrent comment arriver à ce résultat :
// utiliser ignore
#include <fstream>
#include <limits>

int main()
{
    std::ifstream file( "fichier.txt" ); 
    if ( file )
    {
        int lines = 0; 
        while ( file.ignore( std::numeric_limits<int>::max(), '\n' ) ) 
        { 
            ++lines; 
        } 
    }
}
// utiliser std::count et std::istreambuf_iterator
#include <fstream>
#include <algorithm>

int main()
{
    std::ifstream file( "fichier.txt" ); 

    if ( file )
    {
        int lines = std::count(
            std::istreambuf_iterator<char>( file ),
            std::istreambuf_iterator<char>(),
            '\n' );
    }
}

Pourquoi n'ai-je pas le nombre de caractères attendus avec mon fichier ?
Créé le 18/04/2005[haut]
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.
#include <fstream> 

using namespace std; 

int main() 
{ 
    ofstream file_txt( "fichier_txt.txt" ); 
    file_txt << "a\n" "b\n" "c\n";    
    ofstream file_bin( "fichier_bin.txt", ios_base::binary ); 
    file_bin << "a\n" "b\n" "c\n";    
}
Dans cet exemple, sous DOS/Windows, file_txt fait 9 octets, et file_bin en fait 6.


Comment effectuer des lectures / écritures binaires dans un fichier ?
Créé le 17/10/2005[haut]
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.

#include <fstream>
#include <iostream>

std::ofstream FileOut("Toto.txt", std::ios_base::binary);
int xout = 24;
FileOut.write(reinterpret_cast<const char*>(&xout), sizeof(int));
FileOut.close();

std::ifstream FileIn("Toto.txt", std::ios_base::binary);
int xin;
FileIn.read(reinterpret_cast<char*>(&xin), sizeof(int));
FileIn.close();

std::cout << xin << std::endl; // Affiche "24";
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 :

template<typename T>
std::ostream& write(std::ostream& os, const T& value)
{
    return os.write(reinterpret_cast<const char*>(&value), sizeof(T));
}

template<typename T>
std::istream & read(std::istream& is, T& value)
{
    return is.read(reinterpret_cast<char*>(&value), sizeof(T));
}

// Nos appels deviennent donc
write(FileOut, xout);
read(FileIn, xin);
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 polymorphes, etc), il existe des mécanismes plus élaborés et plus efficaces, comme par exemple boost::serialization ou CArchive dans les MFC.



Consultez les autres F.A.Q.


Valid XHTML 1.0 TransitionalValid CSS!

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.