FAQ C++Consultez toutes les FAQ

Nombre d'auteurs : 35, nombre de questions : 368, dernière mise à jour : 17 novembre 2018  Ajouter une question

 

Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums de http://www.developpez.com et de l'expérience personnelle des auteurs.

Je tiens à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur ou si vous souhaitez devenir rédacteur, lisez ceci.

Sur ce, nous vous souhaitons une bonne lecture.


SommaireEntrées / sorties avec les fluxManipulation des fichiers (14)
précédent sommaire suivant
 

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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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 ) ?.

Mis à jour le 3 février 2007 Aurelien.Regat-Barrel HRS

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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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"; 
}

Mis à jour le 18 avril 2005 Aurelien.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):

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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 
}

Mis à jour le 18 avril 2005 Aurelien.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é:

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
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é :

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 :

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
ifstream file( "fichier.txt" ); 
  
// compter le nombre de lignes 
int count = 0; 
  
string line; 
while ( getline( file, line ) ) 
{ 
    ++count; 
}

Mis à jour le 18 avril 2005 Aurelien.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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
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 ; 
}

Mis à jour le 19 octobre 2004 Aurelien.Regat-Barrel dj.motte

Pour que l'expression suivante compile :

Code c++ : Sélectionner tout
1
2
3
4
5
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* :

Code c++ : Sélectionner tout
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 :

Code c++ : Sélectionner tout
1
2
3
4
5
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

Code c++ : Sélectionner tout
1
2
3
4
5
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

Code c++ : Sélectionner tout
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).

Mis à jour le 19 octobre 2004 Aurelien.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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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é.

Mis à jour le 19 octobre 2004 Aurelien.Regat-Barrel LFE

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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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 ?.

Mis à jour le 19 octobre 2004 Aurelien.Regat-Barrel ovh

Voici deux exemples qui illustrent comment arriver à ce résultat :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 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;  
        }  
    } 
}
Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 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' ); 
    } 
}

Mis à jour le 19 octobre 2004 Aurelien.Regat-Barrel Laurent Gomila

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 deux 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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
#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.

Mis à jour le 18 avril 2005 Aurelien.Regat-Barrel

Il est possible d'effectuer une lecture ou une écriture en mode texte dans un flux std::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().

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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 :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.

Mis à jour le 17 octobre 2005 Laurent Gomila

Le code suivant illustre des opérations courantes effectuées sur un nom de fichier :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <string> 
#include <iostream> 
  
int main() 
{ 
    using namespace std; 
  
    string file_name = "fichier.txt"; 
  
    size_t ext_pos = file_name.find_last_of( '.' ); 
    if ( ext_pos != string::npos ) 
    { 
        // 3 manières d'extraire l'entension 
        string ext1 = file_name.substr( ext_pos ); 
        string ext2( file_name, ext_pos ); 
        string ext3; 
        ext3.assign( file_name, ext_pos, file_name.size() - ext_pos ); 
  
        // 3 manières d'extraire le nom 
        string name1 = file_name.substr( 0, ext_pos ); 
        string name2( file_name, 0, ext_pos ); 
        string name3 = file_name; 
        name3.resize( ext_pos ); 
  
        // remplacer le nom 
        string file_name2 = file_name; // copie pour préserver l'original 
        file_name2.replace( 0, ext_pos, "remplace" ); 
  
        // remplacer l'extension 
        string file_name3 = file_name; // copie pour préserver l'original 
        file_name3.replace( ext_pos + 1, file_name3.size() - 1, "dat" ); 
  
        // insérer un mot entre le nom du fichier et son extension 
        string file_name4 = file_name; // copie pour préserver l'original 
        file_name4.insert( ext_pos, "-modif" ); 
  
        // afficher les résultats 
        cout << ext1 << '\n'; // ".txt" 
        cout << ext2 << '\n'; // ".txt" 
        cout << ext3 << '\n'; // ".txt" 
        cout << name1 << '\n'; // "fichier" 
        cout << name2 << '\n'; // "fichier" 
        cout << name3 << '\n'; // "fichier" 
        cout << file_name2 << '\n'; // "remplace.txt" 
        cout << file_name3 << '\n'; // "fichier.dat" 
        cout << file_name4 << '\n'; // "fichier-modif.txt" 
    } 
}

Mis à jour le 19 octobre 2004 Aurelien.Regat-Barrel

Une fois la fin de fichier atteinte par une première lecture, le flux se met dans un état invalide, ce qui empêche la réussite de toute lecture ultérieure.

Afin de pouvoir lire à nouveau le même flux, il faut donc :

  • le remettre dans un état valide (avec la fonction clear()) ;
  • le rembobiner (avec la fonction seekg()).


Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <fstream> 
#include <iostream> 
#include <limits> 
  
int CountLines(std::ifstream& File) 
{ 
    int Count = 0; 
    while (File.ignore(std::numeric_limits<int>::max(), '\n'))  
        ++Count;  
  
    return Count; 
} 
  
int main() 
{ 
    std::ifstream File("fichier.txt"); 
  
    std::cout << CountLines(File) << std::endl; 
  
    File.clear(); 
    File.seekg(0, std::ios::beg); 
  
    std::cout << CountLines(File) << std::endl; 
  
    return 0; 
}

Mis à jour le 15 octobre 2009 Laurent Gomila

Les flux gèrent une notion d'erreur, et en particulier, quand on essaye de lire au-delà de la fin d'un fichier, un flag indiquant cela est positionné. Cette situation arrive très souvent, puisque c'est ainsi qu'on détecte qu'on est arrivé à la fin. Une fois en état d'erreur, la plupart des opérations sur les flux ne font plus rien.

En C++98, la seule manière de sortir de cet état est d'appeler file.clear(); avant de faire d'autres opérations, dont seekg.
En C++11, seekg peut fonctionner directement si l'erreur était liée à une fin de fichier, et enlève alors ce flag.

Mis à jour le 6 juillet 2014 JolyLoic

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

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 © 2019 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et 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.

 
Contacter le responsable de la rubrique C++

Partenaire : Hébergement Web