views:

168

answers:

4

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;

  int p = stream.tellg();  // remember where we are

  stream.seekg(0, std::ios_base::end); // go to the end
  int 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.

EDIT:

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...)
+2  A: 

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();
Matteo Italia
Think you meant `s=os.str()`...
Roddy
Ooops... fixed, thank you. :)
Matteo Italia
@Matteo - 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!
Roddy
+12  A: 

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)

Cubbi
It could still be a one-liner if you want: `string s = string(...)`.
Mike Seymour
Thanks. Can you elaborate on what that's doing? Doesn't eos need initializing somehow?
Roddy
@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
Cubbi
Thanks. Accepting this answer, as it's taught me more!
Roddy
+3  A: 

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) );
wheaties
Note that `istream_iterator<char>` will drop all whitespace and non-printable characters. With `istreambuf_iterator<char>`, this would work, though.
Cubbi
Thanks! I've made the change to the answer.
wheaties
+1  A: 

the .ReadToEnd() method in System.IO.StreamReader (.Net) is basically implemented the same way..:

public override string ReadToEnd() {
    StringBuilder builder = new StringBuilder(this.charLen - this.charPos);
    do {
        builder.Append(this.charBuffer, this.charPos, this.charLen - this.charPos);
        this.charPos = this.charLen;
        this.ReadBuffer();
    }
    while (this.charLen > 0);
    return builder.ToString();
}

but i get your point!

santa
please note that the question is tagged 'C++'
Vlad
does C++ per se exclude .Net? or do you complain about the fact that the few lines are C#? in that case - i feel terribly sorry and excuse myself...
santa