Скопируйте содержимое streambuf в строку

По-видимому, boost::asio::async_read не любит строки, так как единственная перегрузка boost::asio::buffer позволяет мне создавать const_buffer, так что мне приходится читать все в streambuf.
Теперь я хочу скопировать содержимое streambuf в строку, но по-видимому, он поддерживает только запись в char* (sgetn()), создание istream с помощью streambuf и использование getline().

Есть ли другой способ создать строку с содержимым streambufs без чрезмерного копирования?


person tstenner    schedule 18.05.2009    source источник


Ответы (11)


Я не знаю, считается ли это "чрезмерным копированием", но вы можете использовать строковый поток:

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

Например, чтобы прочитать все из стандартного ввода в строку, выполните

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

Кроме того, вы также можете использовать файл istreambuf_iterator. Вам нужно будет измерить, быстрее ли этот или вышеуказанный способ - я не знаю.

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

Обратите внимание, что someStreamBuf выше предназначен для представления streambuf*, поэтому используйте его адрес в соответствии с необходимостью. Также обратите внимание на дополнительные круглые скобки вокруг первого аргумента в последнем примере, чтобы он не интерпретировал его как объявление функции, возвращающее строку и принимающее итератор и еще один указатель на функцию («самый неприятный синтаксический анализ»).

person Johannes Schaub - litb    schedule 18.05.2009
comment
Спасибо, istreambuf_iterator был тем, что я искал. - person tstenner; 18.05.2009
comment
Вот что-то странное. Я не могу предположить, почему, но istreambuf_iterator вырезает последние символы (если в последней строке больше 20 символов). Есть идеи, почему это может быть? - person Ben Usman; 09.02.2010
comment
ss ‹‹ someStreamBuf копирует 11 байтов '0xFFFFFFFFF', которые представляют собой адрес streambuf в памяти. Благодарю. очень полезно :D - person Alex Kremer; 17.07.2013
comment
@AlexKremer, очевидно, имелось в виду someStreamBufPointer - person sehe; 24.09.2015
comment
Начиная с C++11, самой неприятной проблемы синтаксического анализа также можно избежать с помощью istreambuf_iterator<char>{someStreamBuf}. - person MSalters; 05.11.2018

Он действительно скрыт в документах...

Учитывая boost::asio::streambuf b, с 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);
person Sean DeNigris    schedule 08.12.2012
comment
Это хорошо работает! Вы можете использовать b.size() вместо buf_size, если хотите, чтобы все это было помещено в строку. - person Malvineous; 18.05.2013
comment
Предполагается, что к этому ответу относится ответ tstenner выше, где он говорит, что он считается грязным и не будет работать с «обычными потоковыми буферами», или этот способ считается безопасным? - person ChrisPeterson; 11.02.2015

Другая возможность с boost::asio::streambuf — использовать boost::asio::buffer_cast<const char*>() в сочетании с boost::asio::streambuf::data() и boost::asio::streambuf::consume() следующим образом:

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

Это не будет работать с обычными потоковыми буферами и может считаться грязным, но, похоже, это самый быстрый способ сделать это.

person tstenner    schedule 19.05.2009
comment
откуда длина? - person Trevor Hickey; 22.04.2013
comment
Это количество байтов, которое вы взяли из потока. См. boost.org/doc/libs. /1_53_0/doc/html/boost_asio/ссылка/ - person tstenner; 22.04.2013
comment
Что вы имеете в виду под «обычными потоковыми буферами»? Ответ Шона ДеНигриса ниже, основанный на документации, недействителен? - person ChrisPeterson; 11.02.2015
comment
Как примечание для людей, которые видят это сейчас, это будет работать, только если ваш поток имеет один выделенный и заполненный объект буфера. Если поток имеет более одного буфера, вы облажались. Что вам нужно сделать и что вы должны сделать, чтобы это гарантированно работало, - это получить итератор для буфера (буферов), а затем привести итераторы. Вы можете получить эти итераторы, обратившись к buffer-›data().begin(). - person ; 01.05.2015
comment
Я не смог найти никакой информации о том, как boost::asio::steambuf выделяет несколько буферных объектов. Не могли бы вы сказать мне, каковы требования к streambuf для возврата более одного буфера. - person eclipse; 27.08.2015
comment
@eclipse Ты должен отмечать людей! Я не видел вашего комментария, и я действительно вернулся сюда и нашел свой собственный комментарий, потому что я забыл об этом, попался, лол. В любом случае внутренне streambuf будет выделять несколько внутренних буферов, которые охватывает один streambuf. Чтобы быть в безопасности, вы обязательно должны использовать итераторы. Использование прямого приведения, подобного этому, даст вам указатель, который приведет к неопределенному поведению, будь то нарушение порядка данных или длина, которую вы используете для чтения из указателя, выходит за пределы. - person ; 09.02.2016

Для boost::asio::streambuf вы можете найти такое решение:

    boost::asio::streambuf buf;
    /*put data into buf*/

    std::istream is(&buf);
    std::string line;
    std::getline(is, line);

Распечатайте строку:

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

Вы можете найти здесь: http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/async_read_until/overload3.html

person iericzhou    schedule 17.10.2012
comment
Этот ответ определенно лучше, если вы используете async_read_until, потому что этот вызов может возвращать более одной строки данных, и этот ответ правильно оставляет дополнительные данные в streambuf. - person Jim Hunziker; 12.01.2017

Также можно получить символы из asio::streambuf с помощью 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);
person ArtemGr    schedule 01.07.2013

Причина, по которой вы можете создать const_buffer только из std::string, заключается в том, что std::string явно не поддерживает прямую запись на основе указателя в своем контракте. Вы можете сделать что-то злое, например изменить размер строки до определенного размера, затем const_cast константность из c_str() и обращаться с ней как с необработанным буфером char*, но это очень непослушно и когда-нибудь доставит вам неприятности.

Я использую std::vector для своих буферов, потому что, пока вектор не изменяет размер (или вы осторожны с изменением размера), вы можете нормально писать прямые указатели. Если мне нужны некоторые данные в виде std::string, я должен их скопировать, но то, как я работаю с моими буферами чтения, все, что должно продолжаться после обратного вызова чтения, должно быть скопировано в любом случае.

person user121826    schedule 12.06.2009

Более простым ответом было бы преобразовать его в std::string и манипулировать им примерно так

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

Давая очень краткий код для задачи.

person DevMac    schedule 11.11.2016
comment
Это тот же ответ, что и у Шона ДеНигриса... который он опубликовал 4 года назад! - person Arthur Tacca; 06.05.2017

В основном мне не нравятся ответы, которые говорят: «Вы не хотите X, вместо этого вы хотите Y, и вот как сделать Y», но в этом случае я почти уверен, что знаю, чего хотел tstenner.

В Boost 1.66 буфер динамических строк был добавлен, поэтому async_read может напрямую изменять размер и записывать в строковый буфер.

person tstenner    schedule 27.05.2020

Я не видел существующего ответа для чтения ровно n символов в std::stringstream, так что вот как это можно сделать:

std::stringstream ss;
boost::asio::streambuf sb;
const auto len = 10;

std::copy_n(boost::asio::buffers_begin(sb.data()), len,
            std::ostream_iterator<decltype(ss)::char_type>(ss));

Обозреватель компиляторов

person Zitrax    schedule 20.03.2021

Я проверил первый ответ и получил ошибку компилятора при компиляции с использованием "g++ -std=c++11". У меня сработало следующее:

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

Это скомпилировано и запущено.

person Community    schedule 24.06.2016

Я думаю, что это больше похоже на:


streambuf.commit( number_of_bytes_read );

istream istr( &streambuf );
string s;
istr >> s;

Я не смотрел код basic_streambuf, но я считаю, что это должна быть только одна копия в строке.

person Nikolai Fetissov    schedule 18.05.2009
comment
оператор››(istream&,string&) копирует только до первого пробела - person tstenner; 18.05.2009
comment
@tstenner Существует флаг для отключения этого поведения, а также другие флаги, добавляющие другое поведение. - person Etherealone; 17.08.2012
comment
Это решение читается только до тех пор, пока не будет достигнут первый символ пробела, следовательно, пометка - person Werner Erasmus; 21.08.2017