将 streambuf 的内容复制到字符串
Posted
技术标签:
【中文标题】将 streambuf 的内容复制到字符串【英文标题】:Copy a streambuf's contents to a string 【发布时间】:2010-10-27 00:47:13 【问题描述】:显然boost::asio::async_read
不喜欢字符串,因为boost::asio::buffer
的唯一重载允许我创建const_buffer
s,所以我坚持将所有内容读入streambuf。
现在我想将streambuf的内容复制到一个字符串中,但它显然只支持写入char*(sgetn()
),使用streambuf创建一个istream并使用getline()
。
有没有其他方法可以在不过度复制的情况下创建带有streambufs内容的字符串?
【问题讨论】:
【参考方案1】:不知道算不算“过度复制”,不过可以用stringstream:
std::ostringstream ss;
ss << someStreamBuf;
std::string s = ss.str();
例如,要将 stdin 中的所有内容读入字符串,请这样做
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*
,因此请根据需要取其地址。另请注意最后一个示例中第一个参数周围的附加括号,以便它不会将其解释为返回字符串并采用迭代器和另一个函数指针的函数声明(“最令人烦恼的解析”)。
【讨论】:
谢谢,istreambuf_iterator 是我一直在寻找的。span> 这里有点奇怪。我不能假设为什么,但 istreambuf_iterator 会削减最后一个符号(如果最后一行超过 20 个符号)。有什么想法为什么会这样吗? ss @AlexKremer 显然是someStreamBufPointer
的意思
从C++11开始,istreambuf_iterator<char>someStreamBuf
也可以避免“最头疼的解析”问题。【参考方案2】:
它真的被埋了in the docs...
给定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);
【讨论】:
这很好用!如果要将整个内容放入字符串中,可以使用b.size()
而不是 buf_size
。
应该是上面 tstenner 的回答,他说它被认为是脏的并且不能与“正常的流缓冲区”一起工作,或者这种方式被认为是安全的?【参考方案3】:
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);
这不适用于普通的流缓冲区,并且可能被认为是脏的,但它似乎是最快的方法。
【讨论】:
长度从何而来? 这是您从流中获取的字节数。见boost.org/doc/libs/1_53_0/doc/html/boost_asio/reference/… “正常流缓冲”是什么意思? Sean DeNigris 波纹管基于文档的答案无效? 就像现在看到这个的人的注释一样,这只有在你的流有一个分配和填充的缓冲区对象时才有效。如果流有多个缓冲区,那么您就完蛋了。你需要做的和你应该做的事情是保证它的工作是获取一个迭代器到缓冲区,然后转换迭代器。您可以通过访问 buffer->data().begin() 来获取这些迭代器。 @eclipse 你得给人贴标签!我没有看到你的评论,实际上我回到这里并找到了我自己的评论,因为我忘记了这个陷阱哈哈。无论如何,streambuf 内部将分配单个 streambuf 跨越的多个内部缓冲区。为了安全起见,您绝对必须使用迭代器。使用这样的直接向上转换将为您提供一个指针,该指针将导致未定义的行为,无论是数据顺序混乱,还是您用于从指针读取的长度超出范围。【参考方案4】:对于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
【讨论】:
如果您使用async_read_until
,这个答案肯定会更好,因为该调用可以返回多行数据,并且这个答案正确地将多余的数据留在了streambuf
。【参考方案5】:
也可以使用std::basic_streambuf::sgetn
从asio::streambuf
获取字符:
asio::streambuf in;
// ...
char cbuf[in.size()+1]; int rc = in.sgetn (cbuf, sizeof cbuf); cbuf[rc] = 0;
std::string str (cbuf, rc);
【讨论】:
【参考方案6】:您只能从 std::string 创建 const_buffer 的原因是 std::string 在其合约中明确不支持直接基于指针的写入。你可以做一些邪恶的事情,比如将你的字符串调整到某个大小,然后从 c_str() const_cast 常量并将其视为原始 char* 缓冲区,但这很顽皮,总有一天会给你带来麻烦。
我将 std::vector 用于我的缓冲区,因为只要向量不调整大小(或者您小心地处理大小调整),您就可以直接写入指针就可以了。如果我需要一些数据作为 std::string,我必须将其复制出来,但是我处理读取缓冲区的方式,任何需要持续超出读取回调的内容都需要复制出来。
【讨论】:
【参考方案7】:我没有看到将 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));
Compiler explorer
【讨论】:
【参考方案8】:更简单的答案是将其转换为 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;
为任务提供非常简洁的代码。
【讨论】:
这和 Sean DeNigris 的答案是一样的……他 4 年前发布的!【参考方案9】:我最不喜欢说“你不想要 X,你想要 Y 而这是如何做 Y”的答案,但在这种情况下,我很确定我知道 tstenner 想要什么。
在 Boost 1.66 中,添加了 dynamic string buffer 类型,因此 async_read
可以直接调整大小并写入字符串缓冲区。
【讨论】:
你能举个例子吗?官方文档甚至没有展示如何构建一个,因为它是一种模板......【参考方案10】:我测试了第一个答案,在使用“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();
编译并运行。
【讨论】:
【参考方案11】:我觉得更像是:
streambuf.commit( number_of_bytes_read );
istream istr( &streambuf );
string s;
istr >> s;
我没有查看basic_streambuf
代码,但我相信这应该只是字符串中的一个副本。
【讨论】:
operator>>(istream&,string&) 只复制到第一个空格 @tstenner 有标志可以禁用该行为,以及其他标志添加不同的行为。 这个解决方案只读取到第一个空白字符,因此是downmark以上是关于将 streambuf 的内容复制到字符串的主要内容,如果未能解决你的问题,请参考以下文章