如何每次向文件流或文件结构中写入'一个比特(bit)'?
是否可以写入队列并将其刷新到文件中?
在C#或Java中是否可行?
在尝试实现哈夫曼编码的实例时需要这样做。我无法直接向文件中写入比特,因此将它们写入一个位集(bitset)中,然后(当压缩完成时)每次写入其中的8比特块(除了最后一个)。
如何每次向文件流或文件结构中写入'一个比特(bit)'?
是否可以写入队列并将其刷新到文件中?
在C#或Java中是否可行?
在尝试实现哈夫曼编码的实例时需要这样做。我无法直接向文件中写入比特,因此将它们写入一个位集(bitset)中,然后(当压缩完成时)每次写入其中的8比特块(除了最后一个)。
缓冲每个位直到累积了整个字节似乎是一个好主意:
byte b;
int s;
void WriteBit(bool x)
{
b |= (x ? 1 : 0) << s;
s++;
if (s == 8)
{
WriteByte(b);
b = 0;
s = 0;
}
}
当要写入的比特数不是8的倍数时,您只需要处理这种情况。
bool flush
参数和 if (s == 8 || flush)
测试来处理。 - Martin Wickmanboost::dynamic_bitset
和 std::ostream_iterator
以简洁的方式实现所需的结果:#include <fstream>
#include <iterator>
#include <boost/dynamic_bitset.hpp>
typedef boost::dynamic_bitset<unsigned char> Bitset;
// To help populate the bitset with literals */
Bitset& operator<<(Bitset& lhs, bool val) {lhs.push_back(val); return lhs;}
int main()
{
Bitset bitset;
bitset<<0<<1<<0<<1<<0<<1<<0<<1
<<1<<0<<1<<0;
std::ofstream os("data.dat", std::ios::binary);
std::ostream_iterator<char> osit(os);
boost::to_block_range(bitset, osit);
return 0;
}
unsigned char
,将我的dynamic_bitset
块大小设置为8位。您可以通过指定更大的整数类型来增加块大小。
boost::to_block_range
将位集按块转储到给定的输出迭代器中。如果在最后一个块中存在空余的剩余位,则会用零填充它们。AA 05
。这是在小端平台(x64)上的结果。class BitFile(file):
def __init__(self, filename, mode):
super(BitFile, self).__init__(filename, mode)
self.bitCount=0
self.byte = 0
def write(self, bit):
self.bitCount+=1
self.byte = self.byte*2+bit
if self.bitCount%8==0:
super(BitFile, self).write(chr(self.byte))
self.byte=0
def close(self):
if self.bitCount%8!=0:
super(BitFile, self).write(chr(self.byte))
super(BitFile, self).close()
with BitFile("bitfile.bin","w") as bf:
bf.write(1)
bf.write(1)
bf.write(1)
bf.write(0)
bf.write(0)
bf.write(0)
bf.write(0)
bf.write(0)
bf.write(1)
你实际上做不到。我相信问题不在于语言或文件系统,而是硬件问题。处理器设计用于处理字节。你能做的最接近的方法可能是反复写入最后一个字节,右侧填充零,逐个更改它们。
例如,要写入位'11011',可以执行以下操作(Python示例,但任何语言都应该有这样的功能:
f.write(chr(0b10000000))
f.flush()
f.seek(-1)
f.write(chr(0b11000000))
f.flush()
f.seek(-1)
f.write(chr(0b11000000))
f.flush()
f.seek(-1)
f.write(chr(0b11010000))
f.flush()
f.seek(-1)
f.write(chr(0b11011000))
f.flush()
我曾经为哈夫曼解码做过这件事,最终将位写成字符,并将所有内容作为普通的C字符串在内部处理。
这样你就不必担心尾随字节,而且它也更易于人类阅读。此外,检查位更容易,因为只需要访问字符数组(binbuf[123] == '1'
),而不必摆弄位。虽然不是最优化的解决方案,但它很好地解决了我的问题。
显而易见的缺点是这种表示使用更多的内存。
这里的问题在于许多平台没有直接的位访问。它们将位分组成最小的包,通常是字节或字。此外,流设备的协议不便于传输单个位。
处理单个位的常见方法是将它们打包到最小的可移植和(可寻址)可访问单元中。未使用的位通常设置为零。这可以通过二进制算术运算(OR、AND、EXCLUSIVE-OR、NOT等)来实现。
随着现代处理器的出现,位操作会减慢机器的性能。内存很便宜,有了大的寻址空间,位打包的理由变得更加困难。一般来说,位打包是保留给硬件操作(以及传输协议)的。例如,如果处理器的字容量为16位,则处理器可能比在一个字中进行16位操作更快地处理16个字。
此外,请记住,写入和从内存读取通常比从流进行I/O更快。高效的系统在传输数据之前将数据缓冲到内存中。您可能需要考虑在设计中使用此技术。减少I/O操作将提高程序的性能。