102

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...) 
0

7 Answers 7

148

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), {}); 
Sign up to request clarification or add additional context in comments.

7 Comments

It could still be a one-liner if you want: string s = string(...).
Thanks. Can you elaborate on what that's doing? Doesn't eos need initializing somehow?
@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 input
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."
You can now drop <char> as well thanks to template argument deduction.
|
32

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.

6 Comments

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".
Using string::reserve(size_t) would make it even more efficient.
Joey, optimise with -O2. Option -O3 is not for fastest but for compact code as I remember.
@BarnabasSzabolcs: -Os is for compact code, -O3 is for aggressive optimization, while -O2 is less aggressive. See gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
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 stackoverflow.com/questions/19470873/…
|
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(); 

3 Comments

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!
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.
@Tanz87 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?
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) ); 

Comments

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

Comments

1

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

Comments

0

Perhaps this 1 line C++11 solution:

std::vector<char> s{std::istreambuf_iterator<char>{in},{}}; 

1 Comment

std::vector<char> isn't string.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.