How to read entire stream into a std::string?
Asked Answered
S

7

99

I'm trying to read an entire stream (multiple lines) into a string.

I'm using this code, and it works, but it's offending my sense of style... Surely there's an easier way? Maybe using stringstreams?

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.


Actually, a const reference to a string would do as well, and that may make things easier...
const std::string &s(... a miracle occurs here...)
Stenograph answered 8/7, 2010 at 12:20 Comment(0)
U
144

How about

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

(could be a one-liner if not for MVP)

post-2011 edit, this approach is now spelled

std::string s(std::istreambuf_iterator<char>(stream), {});
Unexpected answered 8/7, 2010 at 12:26 Comment(7)
It could still be a one-liner if you want: string s = string(...).Rosalinarosalind
Thanks. Can you elaborate on what that's doing? Doesn't eos need initializing somehow?Stenograph
@Roddy: The string is range-contsructed from istreambuf_iterator, which iterates over unformatted characters until it becomes equal to a default-constructed input iterator, aka "end of stream". See Scott Meyers, Effective STL Item 29: Consider istreambuf_iterators for character-by-character inputUnexpected
Taken from the std::istream_iterator documentation "Notes: When reading characters, std::istream_iterator skips whitespace by default (unless disabled with std::noskipws or equivalent), while std::istreambuf_iterator does not. In addition, std::istreambuf_iterator is more efficient, since it avoids the overhead of constructing and destructing the sentry object once per character."Villada
Reading byte by byte is not quite efficient from the performance POV. Is there a better solution which reads in larger chunks? Perhaps taking advantage of SSE?Mayworm
@Mayworm this doesn't read byte-by-byte, as can be seen with strace. There are of course plenty of other ways to do file I/O, e.g. one could just map that file into memory (such as mapped_file_source )Unexpected
You can now drop <char> as well thanks to template argument deduction.Arcturus
C
31

I'm late to the party, but here is a fairly efficient solution:

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;
}

I did some benchmarking, and it turns out that the std::istreambuf_iterator technique (used by the accepted answer) is actually much slower. On gcc 4.4.5 with -O3, it's about a 4.5x difference on my machine, and the gap becomes wider with lower optimization settings.

Clank answered 12/2, 2011 at 5:59 Comment(6)
Indeed more efficient than my answer, as a proper block-wise read would be. OP wanted the "easy" way though, which is often the opposite of "fast".Unexpected
Using string::reserve(size_t) would make it even more efficient.Pampero
Joey, optimise with -O2. Option -O3 is not for fastest but for compact code as I remember.Jephum
@BarnabasSzabolcs: -Os is for compact code, -O3 is for aggressive optimization, while -O2 is less aggressive. See gcc.gnu.org/onlinedocs/gcc/Optimize-Options.htmlClank
In some cases you need to experiment with different settings. Certain optimizations in certain cases decrease the speed. (My comment on O2/O2 is also wrong based on this argument.) See eg #19471373Jephum
@Pampero How could you know the size_t with a std::istream ?Intersidereal
D
23

You could do

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

but I don't know if it's more efficient.

Alternative version:

std::string s;
std::ostringstream os;
stream>>os.rdbuf();
s=os.str();
Diamonddiamondback answered 8/7, 2010 at 12:26 Comment(3)
Thanks. as a solution, I find this really simple and readable, and I'm using it. However, I accepted Cubbi's answer as I learnt a lot from it!Stenograph
Yes, this is the only "read until eofbit" method. The other methods (istream::get(streambuf*) and std::getline(istream, string)) only read until a given delimiter character.Striction
@Striction You say 'this is the only "read until eofbit" method' but Cubbi's answer (the top voted and accepted as answer) does also read until EOF. Do you think that it won't?Erskine
C
11

You can try using something from algorithms. I have to get ready for work but here's a very quick stab at things (there's got to be a better way):

copy( istreambuf_iterator<char>(stream), istreambuf_iterator<char>(), back_inserter(s) );
Chaetognath answered 8/7, 2010 at 12:29 Comment(0)
C
2

Well, if you are looking for a simple and 'readable' way to do it. I would recomend add/use some high level framework on your project. For that I's always use Poco and Boost on all my projects. In this case, with Poco:

    string text;
    FileStream fstream(TEXT_FILE_PATH);
    StreamCopier::copyToString(fstream, text);
Cottonseed answered 21/7, 2016 at 21:23 Comment(0)
P
0

Perhaps this 1 line C++11 solution:

std::vector<char> s{std::istreambuf_iterator<char>{in},{}};
Pentapody answered 5/12, 2016 at 23:43 Comment(1)
std::vector<char> isn't string.Hollingsworth
H
0

What about to use getline with delimiter? The next code helps me to read whole std::cin into string on ubuntu with g++-10.

#include <iostream>
#include <string>

using namespace std;

int main() {
    string s;

    getline(cin, s, {}); //the whole stream into variable s

    return 0;
}
Hafer answered 29/7, 2020 at 14:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.