我使用霍夫曼算法开发文件压缩器,现在我遇到的问题是:
将算法用于单词: stackoverflow,我得到以下结果:
a,c,e,f,k,l,r,s,t,v,w = 1 time repeated
o = 2 times repeated
a,c,e,f,k,l,r,s,t,v,w = 7.69231%
and
o = 15.3846%
所以我开始插入二进制树,这将得到结果:
o=00
a=010
e=0110
c=0111
t=1000
s=1001
w=1010
v=1011
k=1100
f=1101
r=1110
l=1111
表示树中字符的路径,假设0为左,1为右。
然后单词“stackoverflow”将是: 100110000100111010011111000010110110111011011111001010
并且,我希望将整个值放入一个二进制文件中,这将导致47位,这恰好是6字节,但是我只能使它成为47字节,因为最小值可以放入fwrite或fprintf的文件是1byte,使用sizeof(某事物)。
比我的问题是:如何在我的文件中只打印一个位?
答案 0 :(得分:5)
只需将“标题”写入文件:位数,然后将这些位“打包”为填充最后一位的字节。这是一个样本。
#include <stdio.h>
FILE* f;
/* how many bits in current byte */
int bit_counter;
/* current byte */
unsigned char cur_byte;
/* write 1 or 0 bit */
void write_bit(unsigned char bit)
{
if(++bit_counter == 8)
{
fwrite(&cur_byte,1,1,f);
bit_counter = 0;
cur_byte = 0;
}
cur_byte <<= 1;
cur_byte |= bit;
}
int main()
{
f = fopen("test.bits", "w");
cur_byte = 0;
bit_counter = 0;
/* write the number of bits here to decode the bitstream later (47 in your case) */
/* int num = 47; */
/* fwrite(num, 1, 4, f); */
write_bit(1);
write_bit(0);
write_bit(0);
/* etc... - do this in a loop for each encoded character */
/* 100110000100111010011111000010110110111011011111001010 */
if(bit_counter > 0)
{
// pad the last byte with zeroes
cur_byte <<= 8 - bit_counter;
fwrite(&cur_byte, 1, 1, f);
}
fclose(f);
return 0;
}
要做完整的霍夫曼编码器,你当然必须在开头编写位代码。
答案 1 :(得分:2)
这是一种编码问题。问题是文件只能 包含字节 - 因此1和0在文件中只能是'1'和'0' - 1和0的字符,即字节。
您需要做的是将这些位打包成字节,创建一个包含一组字节中的位的文件。您将无法在文本编辑器中打开文件 - 它不知道您希望将每个位显示为1或0 char ,它将显示无论每个打包字节是什么。您可以使用一个了解如何使用二进制文件的编辑器打开它。例如,vim可以执行此操作。
就额外的尾随字节或文件结束标记而言,您将不得不创建某种编码约定。例如,您可以打包并填充额外的零,就像您在评论中提到的那样,但是按照惯例,前N个字节是元数据 - 例如数据长度,文件中有多少位有趣。这种事情很常见。
答案 2 :(得分:0)
你需要自己管理这个,通过缓冲写入的位,只有当你有一个完整的字节时才实际写入数据。有点像...
void writeBit(bool b)
{
static char buffer=0;
static int bitcount=0;
buffer = (buffer << 1) | (b ? 1:0);
if (++bitcount == 8)
{
fputc(buffer); // write out the byte
bitcount = 0;
buffer = 0;
}
}
上面的内容不是可重入的(并且可能效率很低) - 你需要确保在某种程度上以某种方式刷新任何半写字节,(可能额外写入7个零位),但你应该得到一般的想法。