如何将字节从std::ostream流至std::vector<uint8_t>?求STL优雅实现方式
如何将字节从
std::ostream流转到std::vector<uint8_t>? 很棒的问题!其实STL里已经有现成的方式可以实现你要的功能,不需要额外依赖,而且完全符合你想要的类似std::ostringstream的使用体验,同时完美支持0值字节。下面给你两种优雅的方案,你可以根据场景选择:
方案1:自定义std::streambuf实现无拷贝的字节流(高效首选)
如果你需要处理大量数据,不想有中间拷贝的开销,自定义一个基于std::vector<uint8_t>的流缓冲区是最优解。我们只需要继承std::streambuf,重写两个关键方法,让所有写入的字节直接追加到vector里:
#include <vector> #include <streambuf> #include <ostream> class vector_buf : public std::streambuf { public: // 绑定到目标vector explicit vector_buf(std::vector<uint8_t>& target_vec) : vec_(target_vec) {} protected: // 处理批量写入的字节 std::streamsize xsputn(const char* s, std::streamsize n) override { // 把char*转成uint8_t*,直接插入到vector末尾 vec_.insert(vec_.end(), reinterpret_cast<const uint8_t*>(s), reinterpret_cast<const uint8_t*>(s + n)); return n; // 返回实际写入的字节数 } // 处理单个字符写入(比如用os.put()的时候) int_type overflow(int_type c) override { if (c != traits_type::eof()) { vec_.push_back(static_cast<uint8_t>(c)); } return c; } private: std::vector<uint8_t>& vec_; };
使用的时候,只需要把这个缓冲区挂到std::ostream上,就可以像用普通流一样操作了:
#include <iostream> int main() { std::vector<uint8_t> byte_data; vector_buf buf(byte_data); std::ostream os(&buf); // 随便写什么都可以,包括0值字节和二进制数据 os << "Hello"; os.put('\0'); // 写入一个0字节 os << " World"; // 写入二进制整数 int32_t num = 12345; os.write(reinterpret_cast<const char*>(&num), sizeof(num)); // 现在byte_data里已经存了所有写入的字节,包括0 for (uint8_t b : byte_data) { std::cout << static_cast<int>(b) << " "; } return 0; }
这种方式全程没有中间缓冲区的拷贝,所有字节直接写入目标vector,性能拉满。
方案2:借助std::ostringstream快速转换(简洁易用)
如果你只是偶尔需要这种功能,不想写额外的类,直接用std::ostringstream中转是更简洁的选择。很多人误以为std::string不能保存0值字节,但其实它完全可以——std::string的size()会返回实际写入的总字节数,只有c_str()会在第一个0处截断而已。
#include <sstream> #include <vector> #include <iostream> int main() { std::ostringstream oss; // 和普通流一样写入数据,包括0字节 oss << "Test" << '\0' << "Data"; float pi = 3.14f; oss.write(reinterpret_cast<const char*>(&pi), sizeof(pi)); // 把string的内容转成vector<uint8_t> const std::string& str = oss.str(); std::vector<uint8_t> byte_data(str.begin(), str.end()); // 或者更直接的方式: // std::vector<uint8_t> byte_data(reinterpret_cast<const uint8_t*>(str.data()), // reinterpret_cast<const uint8_t*>(str.data()) + str.size()); // 验证数据 for (uint8_t b : byte_data) { std::cout << static_cast<int>(b) << " "; } return 0; }
这种方式唯一的小缺点是会有一次从string到vector的拷贝,但对于大多数场景来说,这个开销完全可以忽略,胜在代码简洁,不用额外写类。
封装成更易用的工具类
如果需要频繁使用,你可以把方案1的缓冲区和ostream封装成一个类,用起来更顺手:
class vector_ostream : public std::ostream { public: explicit vector_ostream(std::vector<uint8_t>& target_vec) : std::ostream(&buf_), buf_(target_vec) {} private: vector_buf buf_; }; // 使用示例 std::vector<uint8_t> data; vector_ostream vos(data); vos << "Hello" << '\0' << 65; // 直接写入,和普通ostream完全一样
内容的提问来源于stack exchange,提问作者Jennifer M.
相关产品推荐
相关产品推荐

