std::bitset 的二进制序列化
Posted
技术标签:
【中文标题】std::bitset 的二进制序列化【英文标题】:Binary Serialization of std::bitset 【发布时间】:2011-07-12 04:56:03 【问题描述】:std::bitset
有一个to_string()
方法,用于序列化为基于char
的1
s 和0
s 字符串。显然,这对 bitset 中的 每个 位使用单个 8 位 char
,使序列化表示比必要的长 8 倍。
我想以二进制表示形式存储位集以节省空间。 to_ulong()
方法仅在我的位集中少于 32 位时才相关。我有数百个。
我不确定是否要在对象(地址)本身上使用memcpy()
/std::copy()
,因为它假定对象是 POD。
API 似乎没有为我可以从中获取地址的内部数组表示提供句柄。
我还想要从二进制表示中反序列化位集的选项。
我该怎么做?
【问题讨论】:
大端还是小端?有很大的不同(又名位排序)? 并非如此。如果将 bitset 保存为字符数组,则对 Endianess 没有直接影响。 【参考方案1】:除了转换为字符串并对字符串进行自己的序列化(将 8 个字符的块分组为单个序列化字节)之外,我看不到其他明显的方法。
编辑:更好的是使用operator[]
遍历所有位并手动对其进行序列化。
【讨论】:
我想避免手动位旋转,因为表示已经编码在内部连续数组中。【参考方案2】:编辑:以下内容无法按预期工作。显然,“二进制格式”实际上是指“二进制的 ASCII 表示”。
您应该能够使用operator<<
将它们写入std::ostream
。它说here:
[Bitsets]也可以直接从二进制格式的流中插入和提取。
【讨论】:
是的,我看到了这条评论。在我的 bitset 实现中,operator 对了,我刚测试过,还是不行。我会更新我的答案。对不起 是的,我刚刚检查了标准,基本上就是这样。 @user634618 这已经很晚了,但我想我会将它添加到其他任何查看此线程的人。 写入 std::ostream。【参考方案3】:为了完整性回答我自己的问题。
显然,没有简单的和可移植的方式来做到这一点。
为简单起见(尽管效率不高),我最终使用了to_string
,然后从字符串的所有 32 位块(以及其余部分*)创建连续的 32 位位集,并在每个位上使用 to_ulong
将这些位收集到二进制缓冲区中。
这种方法让 STL 本身处理比特,尽管它可能不是最有效的方法。
* 请注意,由于std::bitset
是在总位数上模板化的,因此余数位集需要使用一些简单的模板元编程算法。
【讨论】:
您的解决方案肯定比通过读取位自己进行序列化慢几倍...... 也许吧。我得测试一下。 使用 g++ 天真地将位打包到 unsigned char 数组中比仅调用std::bitset::to_string
慢一点(如果一次手动展开 8 位,则比 to_string
更快) .请注意,在调用to_string
之后使用您的解决方案,您仍然需要进行拆分、重建所有位集、调用它们上的to_ulong
...
当您说“天真地将位打包到一个无符号字符数组中”时,您是什么意思?测试每个位和移位?通过手动展开,您的意思是跳过 8 个循环并显式测试每个位 &ing 和移位?
天真的循环是for (int j=0; j<N; j++) result[j>>3] |= (bs[j] << (j&7));
。展开的一次计算并存储一个字节。【参考方案4】:
这是一种可能的方法,它基于通过一次读取/写入一位来显式创建 std::vector<unsigned char>
...
template<size_t N>
std::vector<unsigned char> bitset_to_bytes(const std::bitset<N>& bs)
std::vector<unsigned char> result((N + 7) >> 3);
for (int j=0; j<int(N); j++)
result[j>>3] |= (bs[j] << (j & 7));
return result;
template<size_t N>
std::bitset<N> bitset_from_bytes(const std::vector<unsigned char>& buf)
assert(buf.size() == ((N + 7) >> 3));
std::bitset<N> result;
for (int j=0; j<int(N); j++)
result[j] = ((buf[j>>3] >> (j & 7)) & 1);
return result;
注意,调用反序列化模板函数bitset_from_bytes
,必须在函数调用中指定位集大小N
,例如
std::bitset<N> bs1;
...
std::vector<unsigned char> buffer = bitset_to_bytes(bs1);
...
std::bitset<N> bs2 = bitset_from_bytes<N>(buffer);
如果您真的关心速度,那么一种可以获得某些东西的解决方案是进行循环展开,以便一次完成一个字节的打包,但更好的是编写您自己的 bitset 实现,而不是隐藏内部二进制表示,而不是使用std::bitset
。
【讨论】:
【参考方案5】:根据 gamedev.net 的建议,可以尝试使用 boost::dynamic_bitset,因为它允许访问 bitpacked 数据的内部表示。
【讨论】:
如果没有代码示例,这个答案没有多大用处。查看文档,它似乎只提供unsigned long
块中的字节(除非您指定不同的模板参数),所以我认为您仍然需要一个循环,可能是两个嵌套循环。【参考方案6】:
这可能会对您有所帮助,它是各种序列化类型的一个小例子。 我添加了 bitset 和 raw bit 值,可以像下面这样使用。
(https://github.com/goblinhack/simple-c-plus-plus-serializer 的所有示例)
class BitsetClass
public:
std::bitset<1> a;
std::bitset<2> b;
std::bitset<3> c;
unsigned int d:1; // need c++20 for default initializers for bitfields
unsigned int e:2;
unsigned int f:3;
BitsetClass(void) d = 0; e = 0; f = 0;
friend std::ostream& operator<<(std::ostream &out,
Bits<const class BitsetClass & > const m
out << bits(my.t.a);
out << bits(my.t.b);
out << bits(my.t.c);
std::bitset<6> s(my.t.d | my.t.e << 1 | my.t.f << 3);
out << bits(s);
return (out);
friend std::istream& operator>>(std::istream &in,
Bits<class BitsetClass &> my)
std::bitset<1> a;
in >> bits(a);
my.t.a = a;
in >> bits(my.t.b);
in >> bits(my.t.c);
std::bitset<6> s;
in >> bits(s);
unsigned long raw_bits = static_cast<unsigned long>(s.to_ulong());
my.t.d = raw_bits & 0b000001;
my.t.e = (raw_bits & 0b000110) >> 1;
my.t.f = (raw_bits & 0b111000) >> 3;
return (in);
;
【讨论】:
以上是关于std::bitset 的二进制序列化的主要内容,如果未能解决你的问题,请参考以下文章