第一个答案没有正确回答问题,因为它占用的空间是应有的 8 倍。
你会怎么做?我真的需要它来保存很多真/假值。
最简单的方法:取连续的 8 个布尔值,将它们表示为一个字节,将该字节写入您的文件。这样会节省很多空间。
在文件的开头,您可以写入要写入文件的布尔值的数量;该数字将有助于从文件中读取字节并将它们转换回布尔值!
这是使用两个函数的尝试,它们将使用最少的字节数,而不压缩位集。
template<int I>
void bitset_dump(const std::bitset<I> &in, std::ostream &out)
{
// export a bitset consisting of I bits to an output stream.
// Eight bits are stored to a single stream byte.
unsigned int i = 0; // the current bit index
unsigned char c = 0; // the current byte
short bits = 0; // to process next byte
while(i < in.size())
{
c = c << 1; //
if(in.at(i)) ++c; // adding 1 if bit is true
++bits;
if(bits == 8)
{
out.put((char)c);
c = 0;
bits = 0;
}
++i;
}
// dump remaining
if(bits != 0) {
// pad the byte so that first bits are in the most significant positions.
while(bits != 8)
{
c = c << 1;
++bits;
}
out.put((char)c);
}
return;
}
template<int I>
void bitset_restore(std::istream &in, std::bitset<I> &out)
{
// read bytes from the input stream to a bitset of size I.
/* for debug */ //for(int n = 0; n < I; ++n) out.at(n) = false;
unsigned int i = 0; // current bit index
unsigned char mask = 0x80; // current byte mask
unsigned char c = 0; // current byte in stream
while(in.good() && (i < I))
{
if((i%8) == 0) // retrieve next character
{ c = in.get();
mask = 0x80;
}
else mask = mask >> 1; // shift mask
out.at(i) = (c & mask);
++i;
}
}
请注意,可能使用位集使用的内存部分的 reinterpret_cast 作为字符数组也可以工作,但它可能不是可移植的跨系统,因为您不知道位集的表示是什么(字节序?)
如果您想要最支持转换为二进制的 bitset 类,并且您的 bitset 大于 unsigned long 的大小,那么最好使用boost::dynamic_bitset。(如果您担心节省空间,我认为它超过 32 位甚至 64 位)。
从 dynamic_bitset 您可以使用 to_block_range 将位写入底层整数类型。您可以使用 from_block_range 或其构造函数从 BlockInputIterator 或通过调用 append() 从块中构造返回的 dynamic_bitset。
现在你有了原生格式(块)的字节,你仍然有将它写入流并读回的问题。
您将需要首先存储一些“标题”信息:您拥有的块数以及可能的字节序。或者您可以使用宏转换为标准字节序(例如 ntohl,但理想情况下,您将使用对您最常见的平台无操作的宏,因此如果这是小端序,您可能希望以这种方式存储并仅转换为大端系统)。
(注意:我假设 boost::dynamic_bitset 标准地以相同的方式转换整数类型,而不管底层字节序。他们的文档没有说)。
将数字二进制写入流使用os.write( &data[0], sizeof(Block) * nBlocks )
和读取使用是。read( &data[0], sizeof(Block) * nBlocks )
假定数据在哪里,vector<Block>
并且在读取之前您必须这样做data.resize(nBlocks)
(不是reserve()
)。(你也可以用istream_iterator
or做一些奇怪的事情,istreambuf_iterator
但 resize() 可能更好)。
一种方法可能是:
std::vector<bool> data = /* obtain bits somehow */
// Reserve an appropriate number of byte-sized buckets.
std::vector<char> bytes((int)std::ceil((float)data.size() / CHAR_BITS));
for(int byteIndex = 0; byteIndex < bytes.size(); ++byteIndex) {
for(int bitIndex = 0; bitIndex < CHAR_BITS; ++bitIndex) {
int bit = data[byteIndex * CHAR_BITS + bitIndex];
bytes[byteIndex] |= bit << bitIndex;
}
}
请注意,这假设您不关心位布局最终在内存中,因为它不会对任何内容进行调整。但是,只要您还序列化实际存储的位数(以涵盖位数不是 CHAR_BITS 倍数的情况),您就可以反序列化与原来完全相同的位集或向量这样.
(我对桶大小的计算不满意,但现在是凌晨 1 点,我很难想出更优雅的东西)。
#include "stdio"
#include "bitset"
...
FILE* pFile;
pFile = fopen("output.dat", "wb");
...
const unsigned int size = 1024;
bitset<size> bitbuffer;
...
fwrite (&bitbuffer, 1, size/8, pFile);
fclose(pFile);
两种选择:
花更多的英镑(或便士,更有可能)购买更大的磁盘。
编写一个例程,一次从位集中提取 8 位,将它们组合成字节,然后将它们写入您的输出流。