You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中bz2模块无法正确重新压缩的问题排查

为什么无法将BZ2解压块重新压缩回原格式?

问题场景

我用以下代码读取并解压BZ2压缩文件:

offset = 24

# 打开文件
fobj = open(filey,'rb')

# 读取数据
buffer = fobj.read()

# 执行BZ2解压
buffer_unbzip,places_to_bzip = bzip_blocks_decompress_all(buffer,offset)

解压函数定义:

def bzip_blocks_decompress_all(data,offset):
    import bz2
    frames = bytearray()
    places_to_bzip = []
    while offset < len(data):
        block_cmp_bytes = abs(int.from_bytes(data[offset:offset + 4], 'big', signed=True))
        offset += 4
        # 解压当前块并拼接到frames
        frames += bz2.decompress(data[offset:offset + block_cmp_bytes])
        # 记录原buffer中该压缩块的位置
        places_to_bzip.append([offset,offset+block_cmp_bytes])
        offset += block_cmp_bytes
        
    return frames,places_to_bzip

之后尝试用places_to_bzip记录的位置重新压缩对应块:

# 提取原压缩块和解压后对应位置的数据
a1 = buffer[places_to_bzip[0][0]:places_to_bzip[0][1]]
a2 = buffer_unbzip[places_to_bzip[0][0]:places_to_bzip[0][1]]

# 重新压缩a2
a3 = bz2.compress(a2)
print(len(a1))
print(len(a2))
print(len(a3))

输出结果:

104
104
70

显然重新压缩后的结果和原压缩块不一致,测试输出的a1和a2如下:

print(a1)
b'BZh51AY&SY\xe6\xb1\xacS\x00\x00\x02_\xab\xfe(@\x00\x10\x00@\x04\x00@\x00@\x800\x02\x00\x00\x01\x00@\x08\x00\x00\x18 \x00T4\x8d\x004\x01\xa0\x91(\x01\x90\xd3\xd2\x14\xac\xd6v\x85\xf0\x0fD\x85\xc3A}\xe09\xbc\xe1\x8b\x04Y\xbfb$"\xcc\x13\xc0B\r\x99\xf1Qa%S\x00|]\xc9\x14\xe1BC\x9a\xc6\xb1L'

print(a2)
bytearray(b'\x00\x0b\x00\x02\x05z\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x00\x00\x00\x00\x002\x04@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01h\x00\x00\x00\x00\x002\x04@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')

问题原因

1. 核心错误:解压后数据的切片逻辑完全错误

places_to_bzip记录的是原压缩文件buffer中压缩块的字节位置,而buffer_unbzip是所有压缩块解压后拼接的全量数据。每个压缩块解压后的长度和原压缩块的长度没有对应关系(通常解压后长度远大于压缩前),用原压缩块的位置/长度去切解压后的全量buffer,得到的a2根本不是a1对应的解压内容。

比如在解压函数中,a1对应的解压内容是bz2.decompress(data[offset:offset + block_cmp_bytes]),这个内容的长度不一定等于原压缩块的104字节,现在用原压缩块的长度去切buffer_unbzip,拿到的不是对应a1的解压内容,重新压缩自然不可能得到a1。

2. BZ2压缩参数不匹配(次要原因)

即使拿到了正确的解压内容,直接用bz2.compress()默认参数压缩,也可能和原压缩块不一致。原压缩块可能使用了特定的压缩级别、块大小或其他参数(比如原压缩用了更低的压缩级别),而Python标准库的bz2.compress()默认使用压缩级别9,会导致压缩结果的字节长度、内容和原块不同。

3. BZ2块的元数据差异

原压缩块a1包含完整的BZ2块结构(比如开头的BZh5标识、CRC校验位、块大小标识等),而bz2.compress()生成的压缩数据可能在元数据格式上和原块有差异,即使解压后内容相同,压缩后的包装格式也会不一样。

解决思路

1. 修正解压时的记录逻辑

在解压函数中,同时记录每个压缩块对应的解压后内容,而不是只记录原压缩块的位置:

def bzip_blocks_decompress_all(data,offset):
    import bz2
    frames = bytearray()
    # 修改为存储每个压缩块的原数据、解压后数据
    block_records = []
    while offset < len(data):
        block_cmp_bytes = abs(int.from_bytes(data[offset:offset + 4], 'big', signed=True))
        offset += 4
        cmp_block = data[offset:offset + block_cmp_bytes]
        decomp_block = bz2.decompress(cmp_block)
        frames += decomp_block
        # 保存原压缩块和解压后块的对应关系
        block_records.append({
            'compressed': cmp_block,
            'decompressed': decomp_block
        })
        offset += block_cmp_bytes
        
    return frames, block_records

后续可以直接拿到每个原压缩块对应的解压内容,再尝试重新压缩对比:

# 取第一个块的记录
record = block_records[0]
a1 = record['compressed']
a2 = record['decompressed']
a3 = bz2.compress(a2)

2. 匹配原压缩参数

如果修正后压缩结果还是和原块不一致,需要尝试调整bz2.compress()的参数,比如压缩级别(compresslevel参数,1-9),或者使用bz2.BZ2Compressor类自定义块大小等参数,尽可能匹配原压缩的参数。

3. 验证解压内容的正确性

先确认bz2.decompress(a1)是否等于正确的解压内容,如果不等,说明切片逻辑肯定错误;如果相等,再排查压缩参数的问题。


内容的提问来源于stack exchange,提问作者Miss_Orchid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 21:07:35