如何从Deflate压缩输出解码块头?解析BTYPE时的疑惑求助
关于Deflate压缩块头比特的疑问与解答
我尝试对一段简单字符串做Deflate压缩后解析输出字节的头比特,代码如下:
char a[50] = "Hello"; z_stream defstream; defstream.zalloc = Z_NULL; defstream.zfree = Z_NULL; defstream.opaque = Z_NULL; defstream.avail_in = (uInt)strlen(a)+1; defstream.next_in = (Bytef *)a; defstream.avail_out = (uInt)sizeof(b); defstream.next_out = (Bytef *)b; deflateInit(&defstream, Z_BEST_COMPRESSION); deflate(&defstream, Z_FINISH); deflateEnd(&defstream); for (int i=0; i<strlen(b); i++) { printf("--- byte[%d]=%hhx\n", i, b[i]); }
输出的前几个字节是:
--- byte[0]=78 --- byte[1]=da --- byte[2]=f3
根据Deflate规范,块头的前3比特分别是:第一位是BFINAL(标记是否为最终块),接下来两位是BTYPE(块类型,00=无压缩,01=固定哈夫曼编码,10=动态哈夫曼编码)。但我计算0x78的前3位是000,这意味着BFINAL=0,BTYPE=00,但这显然有问题——这么短的字符串应该用哈夫曼编码(BTYPE 01或10)才对,我是不是哪里搞错了?
问题根源:你把zlib封装头当成了Deflate块数据!
嘿,这是个非常常见的误区——zlib库输出的不是原始的Deflate比特流,而是带了zlib格式头的封装数据!
咱们拆解一下你输出的前两个字节:
- 第一个字节
0x78:这是zlib的CMF(压缩方法与标志)字段。高4位CM=8(表示用Deflate压缩),低4位CINFO=7(对应窗口大小为2^7=128KB) - 第二个字节
0xda:这是zlib的FLG(标志)字段,里面包含了校验位、压缩级别等额外信息
真正的Deflate压缩块数据,是从第三个字节(也就是你输出里的byte[2]=0xf3)才开始的!
额外的小问题:遍历压缩字节的方式错了
你用strlen(b)来遍历压缩后的字节数组是不对的!因为压缩后的数据流里很可能包含\0字符,strlen会在遇到第一个\0时停止计数,导致你读取的字节数不全。正确的做法是用defstream.total_out来获取压缩后的总字节数——这个字段是zlib帮你统计好的,绝对准确。
验证一下Deflate块头
咱们来解析第三个字节0xf3的二进制:11110011。按照Deflate规范,比特是从最高位到最低位依次读取的,所以前3位拆分后:
- 第一位(最高位)
1→ BFINAL=1(因为这是唯一的压缩块,所以标记为最终块) - 接下来两位
11?不对,哦等等,BTYPE的合法取值是00、01、10,11是预留值,这说明咱们得把后续的字节也结合起来看(Deflate的比特流是跨字节连续的)。不过核心问题已经解决了:你之前解析的根本不是Deflate的块头,而是zlib的封装头!
总结
- zlib输出的不是纯Deflate数据,前两个字节是zlib格式的头信息,Deflate块从第三个字节开始
- 不要用
strlen遍历压缩后的字节数组,用defstream.total_out获取准确的字节数 - 解析Deflate块头时,要从zlib头之后的比特流开始读取
内容的提问来源于stack exchange,提问作者AldoT
相关产品推荐
相关产品推荐

