stack user
stack user

Reputation: 863

copying a fixed length of data from an std::istream to a string

I'd like to copy a fixed length of data from an std::istream to a string:

std::istream & operator >> ( std::istream & is, LogMsg & msg )
{
    // read in 4 bytes - a uint32_t that describes the number of bytes in message:
    // next, read the message bytes into the LogMsg

    typedef std::istream_iterator<unsigned char> Iter;

    Iter            i (is);
    uint32_t        nSize   = 0;
    std::string &   sMsg    = msg.MsgRef();

    is >> nSize;
    sMsg.reserve(nSize);

    std::copy(
        i.begin(), i.begin() + nSize,
        std::back_inserter(sMsg)
    );

    return is;
}

I can't use this solution, as the std::istream_iterator::begin() function on the iterator is c++11 only (I'm constrained to -std=gnu++0x with gcc 4.4.7

So, how can I copy a fixed length of data from an input stream into a string?

I originally loooked at std::istream::read, which seems to fit - it has the following syntax

is.read (buffer,length);

But I don't think you can read into the internal buffers of a string and I'd like to avoid a copy to a temporary buffer. Can I use a streambuf somehow?

Upvotes: 2

Views: 1499

Answers (3)

James Kanze
James Kanze

Reputation: 153899

The obvious solution is std::copy_n:

std::copy_n( std::istreambuf_iterator<char>( is ), size, std::back_inserter( msg ) );

This will only work if you can be sure that the characters are there, however. If you encounter end of file while trying to read the characters, undefined behavior ensues. Which means that although it is an obvious solution, it maybe isn't a good one.

However, in C++11, officially, and in earlier implementations, in practice, you can read into the internal buffer. You must make sure that the string has enough space:

msg.resize( size );
is.read( &msg[0], msg.size() );

(For some reason, there isn't a non-const version of std::string::data(), despite the guarantee of underlying contiguity in C++11.)

Upvotes: 4

David G
David G

Reputation: 96790

Use std::copy_n():

std::copy_n(i, nSize, std::back_inserter(sMsg));

Upvotes: 3

Barry
Barry

Reputation: 302738

You can copy into a string's internal buffer, just make sure it's the right size:

sMsg.resize(nSize);
is.read(&sMsg[0], nSize);

It's less efficient than the solution with begin() since you have to zero-initialize a whole bunch of data that you're immediately overwriting. But it will work pre-C++11.

Upvotes: 3

Related Questions