【问题标题】:How can I print a bit instead of byte in a file?如何在文件中打印位而不是字节?
【发布时间】:2012-06-28 21:24:03
【问题描述】:

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

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

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

【问题讨论】:

  • 文件大小以字节为单位,而不是位。您需要找到一种方法将额外的位打包成一个字节。
  • 我也试过了,考虑到我会在序列中打包最多 8 位以使其成为一个字节,但是我怎样才能表示我的文件正在结束呢?如果我实际上不需要使用 8 位,假设它是单词 test,即 101001,它不会完成一个字节,如果我用 00 填充其他 2 位将代表另一个字符阅读它们,x-x
  • 即使您使用位运算符编写文件,读回文件也将非常困难,因为所有位都会在一起。
  • 不幸的是,大多数系统上的文件大小以字节为单位。所以你需要一种方法来填充文件的末尾。不幸的是,用零填充会导致 stackoverflowo。您是添加新的 符号还是以长度开头。 (长度只需 3 位)因为您可以从文件长度中获取字节数,哦,000 将是 8)。不要担心比特的腰部。操作系统通常以 512 字节或更大的块分配磁盘空间,尽管有时更小。需要更大的开销,就好像你不知道这个词是stackoverflow,就不能知道你正在使用的字母表。

标签: c++ file huffman-code


【解决方案1】:

只需将“标题”写入文件:位数,然后将这些位“打包”到填充最后一个字节的字节中。这是一个示例。

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

当然,要完成完整的 Huffman 编码器,您必须在开头编写位码。

【讨论】:

  • 和roddy post一样,我碰巧最后需要额外的位来完成最后一个字节,这会引发另一个字符的添加
  • 我写过:“在此处写入位数以便稍后解码比特流”。是的,您只需要在开头有一个标头就可以知道流在哪里结束(我建议使用一个简单的位计数器,4 字节整数)。抱歉,这对我来说太明显了,希望你现在清楚了。
【解决方案2】:

这是一个编码问题。问题是文件只能包含字节 - 所以文件中的 1 和 0 只能是 '1' 和 '0' - 1 和 0 的字符是字节。

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

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

【讨论】:

    【解决方案3】:

    您需要自己进行管理,方法是缓冲要写入的位,并且仅在您拥有完整字节时才实际写入数据。比如……

     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 个零位)但您应该大致了解一下。

    【讨论】:

    • 是的,我已经知道了,但是,我怎么能知道终点在哪里?确实不需要的数据量将会增加。
    • 您可以决定按照约定,文件的最后一个字节实际上是填充位数。所以让我们假设你有 30 位。您首先将其填充为 32 位(4 字节)并附加一个值为 2 的最后一个字节(填充数),从而生成 5 字节大小的文件。此方法的最大开销是 2 字节,这没什么。我仍然认为任何压缩算法至少应该呈现一个元数据。(算法版本、原始文件大小、压缩日期、校验和、加密方法......)。
    猜你喜欢
    • 2017-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多