Recherche…


Introduction

Les E / S de fichiers C ++ sont effectuées via des flux . Les principales abstractions sont:

std::istream pour lire du texte.

std::ostream pour écrire du texte.

std::streambuf pour lire ou écrire des caractères.

Entrée formatée utilise un operator>> .

La sortie formatée utilise l' operator<< .

Les flux utilisent std::locale , par exemple pour plus de détails sur le formatage et la traduction entre les codages externes et le codage interne.

Plus sur les flux: <iostream> Library

Ouvrir un fichier

L'ouverture d'un fichier se fait de la même manière pour les 3 flux de fichiers ( ifstream , ofstream et fstream ).

Vous pouvez ouvrir le fichier directement dans le constructeur:

std::ifstream ifs("foo.txt");  // ifstream: Opens file "foo.txt" for reading only.

std::ofstream ofs("foo.txt");  // ofstream: Opens file "foo.txt" for writing only.

std::fstream iofs("foo.txt");  // fstream:  Opens file "foo.txt" for reading and writing.

Vous pouvez également utiliser la fonction membre open() du fichier de flux de données:

std::ifstream ifs;
ifs.open("bar.txt");           // ifstream: Opens file "bar.txt" for reading only.

std::ofstream ofs;
ofs.open("bar.txt");           // ofstream: Opens file "bar.txt" for writing only.

std::fstream iofs;
iofs.open("bar.txt");          // fstream:  Opens file "bar.txt" for reading and writing.

Vous devez toujours vérifier si un fichier a été ouvert avec succès (même lors de l'écriture). Les échecs peuvent inclure: le fichier n'existe pas, le fichier n'a pas les droits d'accès appropriés, le fichier est déjà utilisé, les erreurs de disque se sont produites, le disque a été déconnecté ... La vérification peut être effectuée comme suit:

// Try to read the file 'foo.txt'.
std::ifstream ifs("fooo.txt");  // Note the typo; the file can't be opened.

// Check if the file has been opened successfully.
if (!ifs.is_open()) {
    // The file hasn't been opened; take appropriate actions here.
    throw CustomException(ifs, "File could not be opened");
}

Lorsque le chemin du fichier contient des barres obliques inverses (par exemple, sur le système Windows), vous devez leur échapper correctement:

// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs("c:\\folder\\foo.txt"); // using escaped backslashes
C ++ 11

ou utilisez le littéral brut:

// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs(R"(c:\folder\foo.txt)"); // using raw literal

ou utilisez plutôt des barres obliques:

// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs("c:/folder/foo.txt");
C ++ 11

Si vous souhaitez ouvrir un fichier avec des caractères non-ASCII dans le chemin sur Windows, vous pouvez utiliser l'argument de chemin de caractère large non standard :

// Open the file 'пример\foo.txt' on Windows.
std::ifstream ifs(LR"(пример\foo.txt)"); // using wide characters with raw literal

Lecture d'un fichier

Il existe plusieurs manières de lire les données d’un fichier.

Si vous savez comment les données sont formatées, vous pouvez utiliser l'opérateur d'extraction de flux ( >> ). Supposons que vous avez un fichier nommé foo.txt qui contient les données suivantes:

John Doe 25 4 6 1987
Jane Doe 15 5 24 1976

Ensuite, vous pouvez utiliser le code suivant pour lire ces données à partir du fichier:

// Define variables.
std::ifstream is("foo.txt");
std::string firstname, lastname;
int age, bmonth, bday, byear;

// Extract firstname, lastname, age, bday month, bday day, and bday year in that order.
// Note: '>>' returns false if it reached EOF (end of file) or if the input data doesn't
// correspond to the type of the input variable (for example, the string "foo" can't be
// extracted into an 'int' variable).
while (is >> firstname >> lastname >> age >> bmonth >> bday >> byear)
    // Process the data that has been read.

L'opérateur d'extraction de flux >> extrait chaque caractère et s'arrête s'il trouve un caractère qui ne peut pas être stocké ou s'il s'agit d'un caractère spécial:

  • Pour les types de chaîne, l'opérateur s'arrête à un espace blanc ( ) ou à une nouvelle ligne ( \n ).
  • Pour les nombres, l'opérateur s'arrête à un caractère sans numéro.

Cela signifie que la version suivante du fichier foo.txt sera également lue avec succès par le code précédent:

John 
Doe 25
4 6 1987


Jane
Doe 
15 5
24
1976

L'opérateur d'extraction de flux >> renvoie toujours le flux qui lui est donné. Par conséquent, plusieurs opérateurs peuvent être enchaînés afin de lire les données consécutivement. Cependant, un courant peut également être utilisé comme une expression booléenne (comme le montre le while en boucle dans le code précédent). C'est parce que les classes de flux ont un opérateur de conversion pour le type bool . Cet opérateur bool() retournera true tant que le flux ne contient aucune erreur. Si un flux entre dans un état d'erreur (par exemple, car plus aucune donnée ne peut être extraite), l'opérateur bool() renverra false . Par conséquent, le while en boucle dans le code précédent est sorti après que le fichier d'entrée a été lu à sa fin.

Si vous souhaitez lire un fichier entier en tant que chaîne, vous pouvez utiliser le code suivant:

// Opens 'foo.txt'.
std::ifstream is("foo.txt");
std::string whole_file;

// Sets position to the end of the file.
is.seekg(0, std::ios::end);

// Reserves memory for the file.
whole_file.reserve(is.tellg());

// Sets position to the start of the file.
is.seekg(0, std::ios::beg);

// Sets contents of 'whole_file' to all characters in the file.
whole_file.assign(std::istreambuf_iterator<char>(is),
  std::istreambuf_iterator<char>());

Ce code réserve un espace pour la string afin de réduire les allocations de mémoire inutiles.

Si vous voulez lire un fichier ligne par ligne, vous pouvez utiliser la fonction getline() :

std::ifstream is("foo.txt");   

// The function getline returns false if there are no more lines.
for (std::string str; std::getline(is, str);) {
    // Process the line that has been read.
}

Si vous voulez lire un nombre fixe de caractères, vous pouvez utiliser la fonction membre read() du flux read() :

std::ifstream is("foo.txt");
char str[4];

// Read 4 characters from the file.
is.read(str, 4);

Après l' exécution d' une commande de lecture, vous devez toujours vérifier si l'état d'erreur drapeau failbit a été défini, car il indique si l'opération a échoué ou non. Cela peut être fait en appelant la fonction membre du flux de fichiers fail() :

is.read(str, 4); // This operation might fail for any reason.

if (is.fail())
    // Failed to read!

Ecrire dans un fichier

Il existe plusieurs manières d’écrire dans un fichier. Le plus simple est d'utiliser un flux de fichiers de sortie ( ofstream ) avec l'opérateur d'insertion de flux ( << ):

std::ofstream os("foo.txt");
if(os.is_open()){
    os << "Hello World!";
}

Au lieu de << , vous pouvez également utiliser la fonction membre du flux du fichier de sortie write() :

std::ofstream os("foo.txt");
if(os.is_open()){
    char data[] = "Foo";

    // Writes 3 characters from data -> "Foo".
    os.write(data, 3);
}

Après avoir écrit à un flux, vous devriez toujours vérifier si l' état d'erreur drapeau badbit a été défini, car il indique si l'opération a échoué ou non. Cela peut être fait en appelant la fonction membre du flux du fichier de sortie bad() :

os << "Hello Badbit!"; // This operation might fail for any reason.
if (os.bad())
    // Failed to write!

Modes d'ouverture

Lors de la création d'un flux de fichiers, vous pouvez spécifier un mode d'ouverture. Un mode d'ouverture est essentiellement un paramètre permettant de contrôler la manière dont le flux ouvre le fichier.

(Tous les modes peuvent être trouvés dans l'espace de noms std::ios .)

Un mode d'ouverture peut être fourni comme second paramètre au constructeur d'un flux de fichiers ou à sa fonction membre open() :

std::ofstream os("foo.txt", std::ios::out | std::ios::trunc);

std::ifstream is;
is.open("foo.txt", std::ios::in | std::ios::binary);

Il convient de noter que vous devez définir ios::in ou ios::out si vous souhaitez définir d'autres indicateurs, car ils ne sont pas implicitement définis par les membres iostream, bien qu'ils aient une valeur par défaut correcte.

Si vous ne spécifiez pas de mode d’ouverture, les modes par défaut suivants sont utilisés:

  • ifstream - in
  • ofstream - out
  • fstream - in et out

Les modes d’ouverture de fichier que vous pouvez spécifier par conception sont les suivants:

Mode Sens Pour La description
app ajouter Sortie Ajoute des données à la fin du fichier.
binary binaire Entrée sortie L'entrée et la sortie se font en binaire.
in contribution Contribution Ouvre le fichier pour la lecture.
out sortie Sortie Ouvre le fichier pour l'écriture.
trunc tronquer Entrée sortie Supprime le contenu du fichier lors de son ouverture.
ate à la fin Contribution Va à la fin du fichier lors de l'ouverture.

Remarque: la définition du mode binary permet de lire / écrire les données exactement telles quelles; Si vous ne la définissez pas, la nouvelle ligne '\n' convertie en une séquence de fin de ligne spécifique à la plate-forme.

Par exemple, sous Windows, la séquence de fin de ligne est CRLF ( "\r\n" ).
Ecrire: "\n" => "\r\n"
Lire: "\r\n" => "\n"

Fermer un fichier

La fermeture explicite d'un fichier est rarement nécessaire en C ++, car un flux de fichiers ferme automatiquement son fichier associé dans son destructeur. Toutefois, vous devez essayer de limiter la durée de vie d'un objet de flux de fichiers, afin qu'il ne conserve pas le descripteur de fichiers ouvert plus longtemps que nécessaire. Par exemple, cela peut être fait en plaçant toutes les opérations sur les fichiers dans une portée propre ( {} ):

std::string const prepared_data = prepare_data();
{
    // Open a file for writing.
    std::ofstream output("foo.txt");

    // Write data.
    output << prepared_data;
}  // The ofstream will go out of scope here.
   // Its destructor will take care of closing the file properly.

L'appel de close() explicitement n'est nécessaire que si vous souhaitez réutiliser le même objet fstream ultérieurement, mais que vous ne souhaitez pas conserver le fichier ouvert entre:

// Open the file "foo.txt" for the first time.
std::ofstream output("foo.txt");

// Get some data to write from somewhere.
std::string const prepared_data = prepare_data();

// Write data to the file "foo.txt".
output << prepared_data;

// Close the file "foo.txt".
output.close();

// Preparing data might take a long time. Therefore, we don't open the output file stream
// before we actually can write some data to it.
std::string const more_prepared_data = prepare_complex_data();

// Open the file "foo.txt" for the second time once we are ready for writing.
output.open("foo.txt");

// Write the data to the file "foo.txt".
output << more_prepared_data;

// Close the file "foo.txt" once again.
output.close();

Flushing un flux

Les flux de fichiers sont mis en mémoire tampon par défaut, de même que de nombreux autres types de flux. Cela signifie que les écritures dans le flux ne peuvent pas entraîner la modification immédiate du fichier sous-jacent. Pour forcer toutes les écritures mises en mémoire tampon à se produire immédiatement, vous pouvez vider le flux. Vous pouvez le faire directement en appelant la méthode flush() ou via le manipulateur std::flush stream:

std::ofstream os("foo.txt");
os << "Hello World!" << std::flush;

char data[3] = "Foo";
os.write(data, 3);
os.flush();

Il y a un flux manipulateur std::endl qui combine l'écriture d'une nouvelle ligne avec le vidage du flux:

// Both following lines do the same thing
os << "Hello World!\n" << std::flush;
os << "Hello world!" << std::endl;

La mise en mémoire tampon peut améliorer les performances d'écriture sur un flux. Par conséquent, les applications qui écrivent beaucoup doivent éviter de vider inutilement. À l'inverse, si les E / S sont effectuées rarement, les applications devraient envisager de les vider fréquemment afin d'éviter que les données ne restent bloquées dans l'objet flux.

Lire un fichier ASCII dans une chaîne std ::

std::ifstream f("file.txt");

if (f)
{
  std::stringstream buffer;
  buffer << f.rdbuf();
  f.close();

  // The content of "file.txt" is available in the string `buffer.str()`
}

La méthode rdbuf() renvoie un pointeur sur un streambuf qui peut être poussé dans le buffer via la fonction membre stringstream::operator<< .


Une autre possibilité (popularisée dans Effective STL par Scott Meyers ) est:

std::ifstream f("file.txt");

if (f)
{
  std::string str((std::istreambuf_iterator<char>(f)),
                  std::istreambuf_iterator<char>());

  // Operations on `str`...
}

Ceci est intéressant car nécessite peu de code (et permet de lire un fichier directement dans un conteneur STL, pas seulement les chaînes), mais peut être lent pour les gros fichiers.

Remarque : les parenthèses supplémentaires autour du premier argument du constructeur de chaîne sont essentielles pour éviter le problème d' analyse le plus frustrant .


Enfin et surtout:

std::ifstream f("file.txt");

if (f)
{
  f.seekg(0, std::ios::end);
  const auto size = f.tellg();

  std::string str(size, ' ');
  f.seekg(0);
  f.read(&str[0], size); 
  f.close();

  // Operations on `str`...
}

qui est probablement l'option la plus rapide (parmi les trois proposées).

Lecture d'un fichier dans un conteneur

Dans l'exemple ci-dessous, nous utilisons std::string et operator>> pour lire les éléments du fichier.

    std::ifstream file("file3.txt");

    std::vector<std::string>  v;

    std::string s;
    while(file >> s) // keep reading until we run out
    {
        v.push_back(s);
    }

Dans l'exemple ci-dessus, nous parcourons simplement le fichier en lisant un "élément" à la fois en utilisant l' operator>> . Ce même effet peut être obtenu en utilisant le std::istream_iterator qui est un itérateur en entrée qui lit un "élément" à la fois dans le flux. De plus, la plupart des conteneurs peuvent être construits à l'aide de deux itérateurs afin de simplifier le code ci-dessus pour:

    std::ifstream file("file3.txt");

    std::vector<std::string>  v(std::istream_iterator<std::string>{file},
                                std::istream_iterator<std::string>{});

Nous pouvons étendre cela pour lire tous les types d'objets que nous aimons en spécifiant simplement l'objet que nous voulons lire en tant que paramètre de modèle pour std::istream_iterator . Ainsi, nous pouvons simplement étendre ce qui précède pour lire des lignes (plutôt que des mots) comme ceci:

// Unfortunately there is  no built in type that reads line using >>
// So here we build a simple helper class to do it. That will convert
// back to a string when used in string context.
struct Line
{
    // Store data here
    std::string data;
    // Convert object to string
    operator std::string const&() const {return data;}
    // Read a line from a stream.
    friend std::istream& operator>>(std::istream& stream, Line& line)
    {
        return std::getline(stream, line.data);
    }
};


    std::ifstream file("file3.txt");

    // Read the lines of a file into a container.
    std::vector<std::string>  v(std::istream_iterator<Line>{file},
                                std::istream_iterator<Line>{});

Lecture d'un `struct` à partir d'un fichier texte formaté.

C ++ 11
struct info_type
{
    std::string name;
    int age;
    float height;
    
    // we define an overload of operator>> as a friend function which
    // gives in privileged access to private data members 
    friend std::istream& operator>>(std::istream& is, info_type& info)
    {
        // skip whitespace
        is >> std::ws;
        std::getline(is, info.name);
        is >> info.age;
        is >> info.height;
        return is;
    }
};

void func4()
{
    auto file = std::ifstream("file4.txt");

    std::vector<info_type> v;

    for(info_type info; file >> info;) // keep reading until we run out
    {
        // we only get here if the read succeeded
        v.push_back(info);
    }

    for(auto const& info: v)
    {
        std::cout << "  name: " << info.name << '\n';
        std::cout << "   age: " << info.age << " years" << '\n';
        std::cout << "height: " << info.height << "lbs" << '\n';
        std::cout << '\n';
    }
}

fichier4.txt

Wogger Wabbit
2
6.2
Bilbo Baggins
111
81.3
Mary Poppins
29
154.8

Sortie:

name: Wogger Wabbit
 age: 2 years
height: 6.2lbs

name: Bilbo Baggins
 age: 111 years
height: 81.3lbs

name: Mary Poppins
 age: 29 years
height: 154.8lbs

Copier un fichier

std::ifstream  src("source_filename", std::ios::binary);
std::ofstream  dst("dest_filename",   std::ios::binary);
dst << src.rdbuf();
C ++ 17

Avec C ++ 17, la méthode standard pour copier un fichier consiste à inclure l’en-tête <filesystem> et à utiliser copy_file :

std::fileystem::copy_file("source_filename", "dest_filename");

La bibliothèque de systèmes de fichiers a été initialement développée en tant que boost.filesystem et finalement fusionnée en ISO C ++ à partir de C ++ 17.

Vérification de la fin du fichier dans une condition de boucle, mauvaise pratique?

eof retourne true qu'après avoir lu la fin du fichier. Il n'indique PAS que la prochaine lecture sera la fin du flux.

while (!f.eof())
{
  // Everything is OK

  f >> buffer;

  // What if *only* now the eof / fail bit is set?

  /* Use `buffer` */
}

Vous pouvez écrire correctement:

while (!f.eof()) 
{  
  f >> buffer >> std::ws;

  if (f.fail())
    break;

  /* Use `buffer` */
}

mais

while (f >> buffer)
{
  /* Use `buffer` */
}

est plus simple et moins sujet aux erreurs.

Autres références:

  • std::ws : supprime les espaces blancs d'un flux d'entrée
  • std::basic_ios::fail : renvoie true si une erreur s'est produite sur le flux associé

Ecriture de fichiers avec des paramètres régionaux non standard

Si vous avez besoin d'écrire un fichier en utilisant des paramètres régionaux différents à la valeur par défaut, vous pouvez utiliser std::locale et std::basic_ios::imbue() pour le faire pour un flux de fichiers spécifique:

Guide d'utilisation:

  • Vous devez toujours appliquer un local à un flux avant d'ouvrir le fichier.
  • Une fois que le flux a été imprégné, vous ne devez pas modifier les paramètres régionaux.

Raisons des restrictions: Si vous modifiez un flux de fichiers avec des paramètres régionaux, le comportement n'est pas défini si les paramètres régionaux actuels ne sont pas indépendants de l'état ou ne pointent au début du fichier.

Les flux UTF-8 (et autres) ne sont pas indépendants de l'état. De plus, un flux de fichiers avec un environnement local UTF-8 peut essayer de lire le marqueur de nomenclature à partir du fichier lorsqu'il est ouvert. il suffit donc d'ouvrir le fichier pour lire les caractères du fichier et ce ne sera pas au début.

#include <iostream>
#include <fstream>
#include <locale>

int main()
{
  std::cout << "User-preferred locale setting is "
            << std::locale("").name().c_str() << std::endl;

  // Write a floating-point value using the user's preferred locale.
  std::ofstream ofs1;
  ofs1.imbue(std::locale(""));
  ofs1.open("file1.txt");
  ofs1 << 78123.456 << std::endl;

  // Use a specific locale (names are system-dependent)
  std::ofstream ofs2;
  ofs2.imbue(std::locale("en_US.UTF-8"));
  ofs2.open("file2.txt");
  ofs2 << 78123.456 << std::endl;

  // Switch to the classic "C" locale
  std::ofstream ofs3;
  ofs3.imbue(std::locale::classic());
  ofs3.open("file3.txt");
  ofs3 << 78123.456 << std::endl;
}

Le passage explicite aux paramètres régionaux classiques "C" est utile si votre programme utilise des paramètres régionaux par défaut différents et que vous souhaitez garantir un standard fixe pour la lecture et l'écriture de fichiers. Avec un environnement local préféré "C", l'exemple écrit

78,123.456
78,123.456
78123.456

Si, par exemple, les paramètres régionaux préférés sont l'allemand et utilisent par conséquent un format numérique différent, l'exemple écrit

78 123,456
78,123.456
78123.456

(notez la virgule décimale dans la première ligne).



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow