4

我正在使用霍夫曼算法开发文件压缩器,现在我面临一个问题:

通过对单词使用算法: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%

所以我开始将 then 插入到二叉树中,这将得到结果:

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(something)。

我的问题是:我怎样才能在我的文件中只打印一点?

4

3 回答 3

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;
}

当然,要完成完整的霍夫曼编码器,您必须在开始时编写位代码。

于 2012-06-28T21:41:10.163 回答
2

这是一种编码问题。问题是文件只能包含字节——所以 1 和 0 在文件中只能是“1”和“0”——1 和 0 的字符是字节。

您要做的是将这些位打包成字节,创建一个包含一组字节中的位的文件。您将无法在文本编辑器中打开文件 - 它不知道您想要将每个显示为 1 或 0 char,它将显示每个打包字节的结果。不过,您可以使用了解如何处理二进制文件的编辑器打开它。例如,vim可以做到这一点。

至于额外的尾随字节或文件结束标记,您将不得不创建某种编码约定。例如,您可以使用额外的零打包和填充,就像您在评论中提到的那样,但按照惯例,前 N 个字节是元数据 - 例如数据长度,文件中有多少位感兴趣。这种事情很常见。

于 2012-06-28T21:35:00.930 回答
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 个零位)但是您应该得到一般主意。

于 2012-06-28T21:37:43.317 回答