如何逐位读取二进制文件?

How can I read a binary file bit-by-bit?

提问人:M K 提问时间:11/5/2023 最后编辑:chqrlieM K 更新时间:11/6/2023 访问量:92

问:

我正在做一个使用霍夫曼算法压缩/解压缩文本文件的项目。我已经成功地将给定的文本文件压缩为二进制文件 - 我已经检查了 Visual Studio 十六进制编辑器扩展,它工作正常。

但是,我真的不知道如何实现减压。我想做的是一点一点地读取二进制文件,遍历相应的霍夫曼树,并在到达叶子时写一个字符。但是,我不确定如何逐位读取二进制文件(我知道它不能直接按原样完成:当然,我必须首先进行位操作以实现压缩)。

我尝试使用该函数将位存储到缓冲区中,但坦率地说,我认为我误解了它的工作原理,我不确定要为缓冲区提供多少内存,也不确定如何从缓冲区(即数组)中检索位。fread()char

提前感谢您的任何帮助。

C 制二进制文件 霍夫曼代码

评论

3赞 Retired Ninja 11/5/2023
你不能读取一个位,你必须至少读取一个字节。从那里,您可以使用按位运算隔离各个位。
0赞 S3DEV 11/5/2023
“我想我误解了它是如何工作的”......这是文档的链接。
1赞 Mark Adler 11/5/2023
你是怎么一点一点地写出霍夫曼密码的?同样的事情,只是相反。

答:

3赞 chqrlie 11/5/2023 #1

下面是一个使用缓冲区的简单读取包装器。它从流中逐个读取位,从第一个字节的最低有效位开始:int

// Read the next bit from the stream.
// `buffer` holds the remaining bits from the last byte read
// initial value of `buffer` should be `0`.
// Return the bit value or `EOF` at end of file
int read_next_bit(FILE *stream, int *buffer)
{
    int bits = *buffer;
    if (bits <= 1) {
        // either no bits left or EOF reached already
        if (bits == EOF) {
            return EOF;
        }
        // get the next byte from the stream
        bits = getc(stream);
        if (bits == EOF) {
            // if EOF, make it sticky and return it
            return *buffer = EOF;
        }
        // set the guard bit
        bits |= 0x100;
    }
    // shift the current bit out of the buffer
    *buffer = bits >> 1;
    // and return it.
    return bits & 1;
}

此函数尝试最大程度地减少内存读取、写入和测试次数。

这是一种替代方法,它使用结构将比特流封装在数组中,以缓冲文件内容。

#include <stdio.h>
#include <stdbool.h>

typedef struct bitstream {
    FILE *stream;
    int pos, len;
    unsigned char buf[BUFSIZ];
} bitstream;

#define LSB_FIRST  1
#define MSB_FIRST  2
#define BIT_ORDER  LSB_FIRST  // define as appropriate

// open a bitstream, return a boolean success indicator
bool bitstream_open(bitstream *bt, const char *filename) {
    bt->len = bt->pos = 0;
    bt->stream = fopen(filename, "rb");
    return bt->stream != NULL;
}

// close a bitstream
void bitstream_close(bitstream *bt) {
    if (bt->stream) {
        bt->len = bt->pos = 0;
        fclose(bt->stream);
        bt->stream = NULL;
    }
}

// read a bit from a bitstream:
// return EOF at end of file
//   otherwise return the next bit
int bitstream_read(bitstream *bt) {
    int pos = bt->pos;
    if (pos >= bt->len) {
        bt->pos = pos = 0;
        bt->len = fread(bt->buf, sizeof bt->buf, bt->stream) * 8;
        if (bt->len == 0)
            return EOF;
    }
    bt->pos++;
#if BIT_ORDER == LSB_FIRST
    return (buf[pos >> 3] >> (pos & 7)) & 1;
#else
    return (buf[pos >> 3] >> (7 - (pos & 7))) & 1;
#endif
}