web-dev-qa-db-fra.com

Quelle est la façon la plus élégante de lire un fichier texte avec c ++?

Je souhaite lire l'intégralité du contenu d'un fichier texte dans un std::string objet avec c ++.

Avec Python, je peux écrire:

text = open("text.txt", "rt").read()

C'est très simple et élégant. Je déteste les trucs moches, donc j'aimerais savoir - quelle est la façon la plus élégante de lire un fichier texte avec C++? Merci.

57
Fang-Pen Lin

Il y a plusieurs façons de choisir celle qui est la plus élégante pour vous.

Lecture en char *:

ifstream file ("file.txt", ios::in|ios::binary|ios::ate);
if (file.is_open())
{
    file.seekg(0, ios::end);
    size = file.tellg();
    char *contents = new char [size];
    file.seekg (0, ios::beg);
    file.read (contents, size);
    file.close();
    //... do something with it
    delete [] contents;
}

Dans std :: string:

std::ifstream in("file.txt");
std::string contents((std::istreambuf_iterator<char>(in)), 
    std::istreambuf_iterator<char>());

En vecteur <char>:

std::ifstream in("file.txt");
std::vector<char> contents((std::istreambuf_iterator<char>(in)),
    std::istreambuf_iterator<char>());

En chaîne, en utilisant stringstream:

std::ifstream in("file.txt");
std::stringstream buffer;
buffer << in.rdbuf();
std::string contents(buffer.str());

file.txt n'est qu'un exemple, tout fonctionne aussi bien pour les fichiers binaires, assurez-vous simplement d'utiliser ios :: binary dans le constructeur ifstream.

124
Milan Babuškov

Il y a n autre fil sur ce sujet.

Mes solutions à partir de ce fil (les deux one-liners):

Le Nice (voir la deuxième solution de Milan):

string str((istreambuf_iterator<char>(ifs)), istreambuf_iterator<char>());

et le jeûne:

string str(static_cast<stringstream const&>(stringstream() << ifs.rdbuf()).str());
11
Konrad Rudolph

Vous semblez parler de l'élégance comme d'une propriété certaine du "petit code". Ceci est bien sûr subjectif dans une certaine mesure. Certains diraient qu'omettre toute gestion des erreurs n'est pas très élégant. Certains diraient qu'un code clair et compact que vous comprenez tout de suite est élégant.

Écrivez votre propre fonction/méthode à une ligne qui lit le contenu du fichier, mais rendez-le rigoureux et sûr sous la surface et vous aurez couvert les deux aspects de l'élégance.

Bonne chance

/ Robert

4
sharkin

Mais attention, une chaîne c ++ (ou plus concrète: une chaîne STL) est aussi petite qu'une chaîne C capable de contenir une chaîne de longueur arbitraire - bien sûr que non!

Jetez un œil au membre max_size () qui vous donne le nombre maximum de caractères qu'une chaîne peut contenir. Il s'agit d'un nombre défini par l'implémentation et peut ne pas être portable entre différentes plates-formes. Visual Studio donne une valeur d'environ 4 Go pour les chaînes, d'autres pourraient vous donner seulement 64 Ko et sur les plates-formes 64 bits, cela pourrait vous donner quelque chose de vraiment énorme! Cela dépend et bien sûr, normalement, vous rencontrerez une exception bad_alloc en raison de l'épuisement de la mémoire longtemps avant d'atteindre la limite de 4gig ...

BTW: max_size () est également membre d'autres conteneurs STL! Il vous donnera le nombre maximum d'éléments d'un certain type (pour lequel vous avez instancié le conteneur) que ce conteneur pourra (théoriquement) contenir.

Donc, si vous lisez un fichier d'origine inconnue, vous devez:
- Vérifiez sa taille et assurez-vous qu'elle est plus petite que max_size ()
- Détecter et traiter les exceptions bad_alloc

Et un autre point: pourquoi voulez-vous lire le fichier dans une chaîne? Je m'attendrais à le traiter davantage en le analysant progressivement ou quelque chose, non? Donc, au lieu de le lire dans une chaîne, vous pourriez aussi bien le lire dans un flux de chaînes (qui est simplement un peu de sucre syntaxique pour une chaîne) et faire le traitement. Mais vous pouvez également effectuer le traitement directement à partir du fichier. Parce que s'il est correctement programmé, le train de chaînes peut être remplacé de manière transparente par un flux de fichiers, i. e. par le fichier lui-même. Ou par tout autre flux d'entrée, ils partagent tous les mêmes membres et opérateurs et peuvent ainsi être échangés de manière transparente!

Et pour le traitement lui-même: il y a aussi beaucoup de choses que vous pouvez avoir automatisées par le compilateur! Par exemple. disons que vous voulez symboliser la chaîne. Lors de la définition d'un modèle approprié, les actions suivantes:
- Lecture à partir d'un fichier (ou d'une chaîne ou de tout autre flux d'entrée)
- Tokenisation du contenu
- en poussant tous les jetons trouvés dans un conteneur STL
- triez les jetons par ordre alphabétique
- élimination des doubles valeurs
peuvent tous (!!) être obtenus en une seule (!) ligne de code C++ (à part le modèle lui-même et la gestion des erreurs)! C'est juste un simple appel de la fonction std :: copy ()! Il suffit de google pour "itérateur de jetons" et vous aurez une idée de ce que je veux dire. Cela me semble donc encore plus "élégant" que la simple lecture d'un fichier ...

1
Don Pedro

J'aime la manière char * de Milan, mais avec std :: string.


#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
using namespace std;

string& getfile(const string& filename, string& buffer) {
    ifstream in(filename.c_str(), ios_base::binary | ios_base::ate);
    in.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit);
    buffer.resize(in.tellg());
    in.seekg(0, ios_base::beg);
    in.read(&buffer[0], buffer.size());
    return buffer;
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        cerr << "Usage: this_executable file_to_read\n";
        return EXIT_FAILURE;
    }
    string buffer;
    cout << getfile(argv[1], buffer).size() << "\n";
}

(avec ou sans le binaire ios_base ::, selon que vous souhaitez que les sauts de ligne soient traduits ou non. Vous pouvez également modifier getfile pour renvoyer simplement une chaîne afin de ne pas avoir à passer de chaîne tampon. Ensuite, testez pour voir si le compilateur optimise la copie lors du retour.)

Cependant, cela pourrait sembler un peu mieux (et être beaucoup plus lent):


#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
using namespace std;

string getfile(const string& filename) {
    ifstream in(filename.c_str(), ios_base::binary);
    in.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit);
    return string(istreambuf_iterator<char>(in), istreambuf_iterator<char>());
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        cerr << "Usage: this_executable file_to_read\n";
        return EXIT_FAILURE;
    }
    cout << getfile(argv[1]).size() << "\n";
}
0
Shadow2531