Tutorial :Copy a streambuf's contents to a string


Apparently boost::asio::async_read doesn't like strings, as the only overload of boost::asio::buffer allows me to create const_buffers, so I'm stuck with reading everything into a streambuf.
Now I want to copy the contents of the streambuf into a string, but it apparently only supports writing to char* (sgetn()), creating an istream with the streambuf and using getline().

Is there any other way to create a string with the streambufs contents without excessive copying?


I don't know whether it counts as "excessive copying", but you can use a stringstream:

std::ostringstream ss;  ss << someStreamBuf;  std::string s = ss.str();  

Like, to read everything from stdin into a string, do

std::ostringstream ss;  ss << std::cin.rdbuf();  std::string s = ss.str();  

Alternatively, you may also use a istreambuf_iterator. You will have to measure whether this or the above way is faster - i don't know.

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

Note that someStreamBuf above is meant to represent a streambuf*, so take its address as appropriate. Also note the additional parentheses around the first argument in the last example, so that it doesn't interpret it as a function declaration returning a string and taking an iterator and another function pointer ("most vexing parse").


It's really buried in the docs...

Given boost::asio::streambuf b, with size_t buf_size ...

boost::asio::streambuf::const_buffers_type bufs = b.data();  std::string str(boost::asio::buffers_begin(bufs),                  boost::asio::buffers_begin(bufs) + buf_size);  


Another possibility with boost::asio::streambuf is to use boost::asio::buffer_cast<const char*>() in conjunction with boost::asio::streambuf::data() and boost::asio::streambuf::consume() like this:

const char* header=boost::asio::buffer_cast<const char*>(readbuffer.data());  //Do stuff with header, maybe construct a std::string with std::string(header,header+length)  readbuffer.consume(length);  

This won't work with normal streambufs and might be considered dirty, but it seems to be the fastest way of doing it.


For boost::asio::streambuf you may find a solution like this:

    boost::asio::streambuf buf;      /*put data into buf*/        std::istream is(&buf);      std::string line;      std::getline(is, line);  

Print out the string :

    std::cout << line << std::endl;  

You may find here: http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/async_read_until/overload3.html


One can also obtain the characters from asio::streambuf using std::basic_streambuf::sgetn:

asio::streambuf in;  // ...  char cbuf[in.size()+1]; int rc = in.sgetn (cbuf, sizeof cbuf); cbuf[rc] = 0;  std::string str (cbuf, rc);  


The reason you can only create const_buffer from std::string is because std::string explicitly doesn't support direct pointer-based writing in its contract. You could do something evil like resize your string to a certain size, then const_cast the constness from c_str() and treat it like a raw char* buffer, but that's very naughty and will get you in trouble someday.

I use std::vector for my buffers because as long as the vector doesn't resize (or you are careful to deal with resizing), you can do direct pointer writing just fine. If I need some of the data as a std::string, I have to copy it out, but the way I deal with my read buffers, anything that needs to last beyond the read callback needs to be copied out regardless.


A simpler answer would be to convert it in std::string and manipulate it some what like this

 std::string buffer_to_string(const boost::asio::streambuf &buffer)   {    using boost::asio::buffers_begin;    auto bufs = buffer.data();    std::string result(buffers_begin(bufs), buffers_begin(bufs) + buffer.size());   return result;  }  

Giving a very concise code for the task.


I think it's more like:

  streambuf.commit( number_of_bytes_read );    istream istr( &streambuf );  string s;  istr >> s;  

I haven't looked into the basic_streambuf code, but I believe that should be just one copy into the string.


I tested the first answer and got a compiler error when compiling using "g++ -std=c++11" What worked for me was:

        #include <string>          #include <boost/asio.hpp>          #include <sstream>                     //other code ...          boost::asio::streambuf response;          //more code          std::ostringstream sline;          sline << &response; //need '&' or you a compiler error          std::string line = sline.str();   

This compiled and ran.

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Next Post »