678 votes

Lire tout le fichier ASCII dans std :: string C ++

Je dois lire un fichier entier en mémoire et le placer dans un std::string en C++.

Si je devais le lire dans un char[], la réponse serait très simple:

std::ifstream t;
int length;
t.open("file.txt");      // ouvrir le fichier d'entrée
t.seekg(0, std::ios::end);    // aller à la fin
length = t.tellg();           // indiquer l'emplacement (c'est la longueur)
t.seekg(0, std::ios::beg);    // revenir au début
buffer = new char[length];    // allouer de la mémoire pour un tampon de la dimension appropriée
t.read(buffer, length);       // lire le fichier entier dans le tampon
t.close();                    // fermer la poignée du fichier

// ... Faire des trucs avec le tampon ici ...

Maintenant, je veux faire exactement la même chose, mais en utilisant un std::string au lieu d'un char[]. Je veux éviter les boucles, c'est-à-dire que je ne veux pas:

std::ifstream t;
t.open("file.txt");
std::string buffer;
std::string line;
while(t){
std::getline(t, line);
// ... Ajoutez la ligne au tampon et continuez
}
t.close()

Des idées?

2 votes

Il y aura toujours une boucle impliquée, mais elle peut être implicite en tant que partie de la bibliothèque standard. Est-ce acceptable? Pourquoi essayez-vous d'éviter les boucles?

7 votes

Je crois que l'auteur savait que lire des octets impliquait une boucle. Il voulait simplement un équivalent facile, de style perl gulp. Cela incluait écrire un peu de code.

0 votes

Ce code est bogué, dans le cas où la std::string n'utilise pas un tampon continu pour ses données de chaîne (ce qui est autorisé) : stackoverflow.com/a/1043318/1602642

995voto

Jerry Coffin Points 237758

Il existe quelques possibilités. Celle que j'aime utilise un stringstream comme intermédiaire :

std::ifstream t("file.txt");
std::stringstream buffer;
buffer << t.rdbuf();

Maintenant, le contenu de "file.txt" est disponible dans une chaîne de caractères comme buffer.str().

Une autre possibilité (bien que je ne l'aime certainement pas autant) est beaucoup plus similaire à votre solution d'origine :

std::ifstream t("file.txt");
t.seekg(0, std::ios::end);
size_t size = t.tellg();
std::string buffer(size, ' ');
t.seekg(0);
t.read(&buffer[0], size); 

Officiellement, ce n'est pas nécessaire de fonctionner sous les normes C++98 ou 03 (il n'est pas nécessaire que la chaîne de caractères stocke les données de manière contiguë) mais en fait ça fonctionne avec toutes les implémentations connues, et C++11 et les versions ultérieures nécessitent un stockage contigu, donc c'est garanti pour fonctionner avec elles.

Quant à pourquoi je n'aime pas autant cette dernière solution : premièrement, parce que c'est plus long et plus difficile à lire. Deuxièmement, parce qu'il nécessite que vous initialisiez le contenu de la chaîne avec des données dont vous n'avez pas besoin, puis écrivez immédiatement sur ces données (oui, le temps d'initialisation est généralement négligeable par rapport à la lecture, donc cela ne pose probablement pas de problème, mais pour moi cela semble toujours un peu faux). Troisièmement, dans un fichier texte, la position X dans le fichier ne signifie pas nécessairement que vous aurez lu X caractères pour atteindre ce point - il n'est pas nécessaire de prendre en compte des éléments tels que les traductions de fin de ligne. Sur les systèmes réels qui effectuent de telles traductions (par exemple, Windows), la forme traduite est plus courte que ce qui se trouve dans le fichier (c'est-à-dire que "\r\n" dans le fichier devient "\n" dans la chaîne traduite) donc vous avez simplement réservé un peu d'espace supplémentaire que vous n'utiliserez jamais. Encore une fois, cela ne pose pas vraiment de problème majeur mais ça semble un peu faux quand même.

44 votes

Le trois-lignes fonctionne à merveille!

101 votes

Cela aurait dû être marqué comme la réponse.

38 votes

Note importante pour certains, du moins dans mon implémentation, le code à trois lignes fonctionne au moins aussi bien que l'alternative C fopen pour les fichiers de moins de 50 Ko. Au-delà de cela, il semble perdre rapidement en performance. Dans ce cas, utilisez simplement la deuxième solution.

594voto

Tyler McHenry Points 35551

Mise à jour: Il s'avère que cette méthode, bien qu'elle suive bien les idiomes STL, est en fait étonnamment inefficace! Ne faites pas cela avec de gros fichiers. (Voir: http://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html)

Vous pouvez créer un itérateur streambuf à partir du fichier et initialiser la chaîne avec celui-ci:

#include 
#include 
#include 

std::ifstream t("file.txt");
std::string str((std::istreambuf_iterator(t)),
                 std::istreambuf_iterator());

Je ne sais pas d'où vient la syntaxe t.open("file.txt", "r"). Autant que je sache, ce n'est pas une méthode que std::ifstream possède. On dirait que vous l'avez confondue avec fopen de C.

Édition: Notez également les parenthèses supplémentaires autour du premier argument du constructeur de la chaîne. Elles sont essentielles. Elles évitent le problème connu sous le nom de "analyse la plus contrariante", qui, dans ce cas, ne vous donnera pas réellement une erreur de compilation comme c'est généralement le cas, mais vous donnera des résultats intéressants (à savoir: incorrects).

Suivant le point de KeithB dans les commentaires, voici une façon de le faire qui alloue toute la mémoire à l'avance (plutôt que de compter sur la réallocation automatique de la classe string):

#include 
#include 
#include 

std::ifstream t("file.txt");
std::string str;

t.seekg(0, std::ios::end);   
str.reserve(t.tellg());
t.seekg(0, std::ios::beg);

str.assign((std::istreambuf_iterator(t)),
            std::istreambuf_iterator());

5 votes

Ouvrir est définitivement une méthode de ifstream, cependant le 2ème paramètre est incorrect. cplusplus.com/reference/iostream/ifstream/open

1 votes

Correct. Je disais que ifstream n'a pas de méthode avec la signature open(const char*, const char*)

0 votes

Ceci se contente de rendre la boucle explicite implicite. Étant donné que l'itérateur est un itérateur avant, il sera lu un caractère à la fois. De plus, comme il n'y a aucun moyen pour le constructeur de chaîne de connaître la longueur finale, cela conduira probablement à plusieurs allocations et copies des données.

86voto

mili Points 457

Je pense que la meilleure façon est d'utiliser un flux de chaînes. Simple et rapide !!!

#include <fstream>
#include <iostream>
#include <sstream> //std::stringstream
int main() {
    std::ifstream inFile;
    inFile.open("inFileName"); //ouvrir le fichier d'entrée

    std::stringstream strStream;
    strStream << inFile.rdbuf(); //lire le fichier
    std::string str = strStream.str(); //str contient le contenu du fichier

    std::cout << str << "\n"; //vous pouvez faire ce que vous voulez avec la chaîne !!!
}

8 votes

4 votes

N'oubliez pas de fermer le flux ensuite...

31 votes

@YngveSneenLindal Ou laissez le destructeur le faire automatiquement - profitez de C++!

2voto

yash101 Points 74

J'ai trouvé une autre méthode qui fonctionne avec la plupart des istreams, y compris std::cin!

std::string readFile()
{
    stringstream str;
    ifstream stream("Hello_World.txt");
    if(stream.is_open())
    {
        while(stream.peek() != EOF)
        {
            str << (char) stream.get();
        }
        stream.close();
        return str.str();
    }
}

0voto

chunkyguy Points 1146

Je pourrais le faire de cette manière :

void readfile(const std::string & filepath, std::string & buffer){
    std::ifstream fin(filepath.c_str());
    getline(fin, buffer, char(-1));
    fin.close();
}

Si c'est quelque chose qui doit être désapprouvé, veuillez me faire savoir pourquoi

12 votes

Char(-1) n'est probablement pas une façon portable de désigner la fin de fichier. De plus, les implémentations de getline() ne sont pas obligées de prendre en charge le pseudo-caractère EOF "non valide" en tant que caractère délimiteur, je pense.

0 votes

@reddish ce n'est en effet pas nécessaire, en C++ moderne il est préférable d'utiliser std::char_traits::eof(). Si quelqu'un utilise encore un ancien compilateur... contient la macro EOF.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X