94 votes

Comment lire un flux entier dans une chaîne std::string ?

J'essaie de lire un flux entier (plusieurs lignes) dans une chaîne de caractères.

J'utilise ce code, et il fonctionne, mais il heurte mon sens du style... Il y a sûrement un moyen plus simple ? Peut-être en utilisant des chaînes de caractères ?

void Obj::loadFromStream(std::istream & stream)
{ 
  std::string s;

  std::streampos p = stream.tellg();  // remember where we are

  stream.seekg(0, std::ios_base::end); // go to the end
  std::streamoff sz = stream.tellg() - p;  // work out the size
  stream.seekg(p);        // restore the position

  s.resize(sz);          // resize the string
  stream.read(&s[0], sz);  // and finally, read in the data.

En fait, un const Une référence à une chaîne de caractères ferait aussi l'affaire, et cela pourrait faciliter les choses...

const std::string &s(... a miracle occurs here...)

138voto

Cubbi Points 25339

Et si

std::istreambuf_iterator<char> eos;
std::string s(std::istreambuf_iterator<char>(stream), eos);

(pourrait être une phrase unique si ce n'est pour MVP)

modification post-2011, cette approche est maintenant orthographiée

std::string s(std::istreambuf_iterator<char>(stream), {});

2 votes

Ça peut toujours être une seule phrase si tu veux : string s = string(...) .

1 votes

Merci. Pouvez-vous préciser ce que ça fait ? Est-ce que eos ne doit pas être initialisé d'une manière ou d'une autre ?

15 votes

@Roddy : La chaîne est construite à partir de istreambuf_iterator, qui itère sur les caractères non formatés jusqu'à ce qu'il devienne égal à un itérateur d'entrée construit par défaut, alias "end of stream". Voir Scott Meyers, Effective STL Item 29 : Considérer les istreambuf_iterators pour l'entrée caractère par caractère.

31voto

Joey Adams Points 13049

Je suis en retard pour la fête, mais voici une solution assez efficace :

std::string gulp(std::istream &in)
{
    std::string ret;
    char buffer[4096];
    while (in.read(buffer, sizeof(buffer)))
        ret.append(buffer, sizeof(buffer));
    ret.append(buffer, in.gcount());
    return ret;
}

J'ai fait quelques tests de référence, et il s'avère que la std::istreambuf_iterator technique ( utilisé par la réponse acceptée ) est en fait beaucoup plus lent. Sur gcc 4.4.5 avec -O3 La différence est d'environ 4,5 fois sur ma machine, et l'écart se creuse avec des paramètres d'optimisation plus faibles.

5 votes

En effet, c'est plus efficace que ma réponse, car une lecture correcte par bloc le serait. L'OP voulait la méthode "facile", qui est souvent l'opposé de "rapide".

9 votes

L'utilisation de string::reserve(size_t) le rendrait encore plus efficace.

0 votes

Joey, optimise avec -O2. L'option -O3 n'est pas pour le plus rapide mais pour un code compact si je me souviens bien.

23voto

Matteo Italia Points 53117

Vous pourriez faire

std::string s;
std::ostringstream os;
os<<stream.rdbuf();
s=os.str();

mais je ne sais pas si c'est plus efficace.

Version alternative :

std::string s;
std::ostringstream os;
stream>>os.rdbuf();
s=os.str();

2 votes

Merci. Comme solution, je trouve cela vraiment simple et lisible, et je l'utilise. Cependant, j'ai accepté la réponse de Cubbi car elle m'a beaucoup appris !

0 votes

Oui, c'est la seule méthode de "lecture jusqu'à l'eofbit". Les autres méthodes ( istream::get(streambuf*) y std::getline(istream, string) ) ne sont lus que jusqu'à un caractère de délimitation donné.

0 votes

@Tanz87 Vous dites 'c'est la seule méthode de "lecture jusqu'à eofbit"' mais la réponse de Cubbi (la mieux votée et acceptée comme réponse) lit aussi jusqu'à EOF. Pensez-vous que ce ne sera pas le cas ?

11voto

wheaties Points 20917

Vous pouvez essayer d'utiliser quelque chose des algorithmes. Je dois me préparer pour le travail, mais voici un coup d'essai très rapide (il doit y avoir un meilleur moyen) :

copy( istreambuf_iterator<char>(stream), istreambuf_iterator<char>(), back_inserter(s) );

2voto

wdavilaneto Points 61

Eh bien, si vous cherchez un moyen simple et "lisible" de le faire. Je vous recommande d'ajouter/utiliser un framework de haut niveau sur votre projet. Pour cela, j'utilise toujours Poco et Boost sur tous mes projets. Dans ce cas, avec Poco :

    string text;
    FileStream fstream(TEXT_FILE_PATH);
    StreamCopier::copyToString(fstream, text);

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