在python中的zlib解压缩

时间:2009-08-22 16:24:57

标签: python compression zlib corruption

好的,我有一些由python(2.6)zlib.compress()函数压缩的数据流。当我尝试解压缩它们时,其中一些不会解压缩(zlib错误-5,这似乎是一个“缓冲区错误”,不知道该怎么做)。起初,我以为我已经完成了,但我意识到所有无法解压缩的都是从0x78DA开始的(工作的是0x789C),我环顾四周,似乎是一种不同的zlib压缩 - 幻数根据使用的压缩而变化。我可以用什么来解压缩文件?我是否已经开了?

4 个答案:

答案 0 :(得分:29)

根据RFC 1950,“OK”0x789C与“坏”0x78DA之间的差异在FLEVEL位域中:

  FLEVEL (Compression level)
     These flags are available for use by specific compression
     methods.  The "deflate" method (CM = 8) sets these flags as
     follows:

        0 - compressor used fastest algorithm
        1 - compressor used fast algorithm
        2 - compressor used default algorithm
        3 - compressor used maximum compression, slowest algorithm

     The information in FLEVEL is not needed for decompression; it
     is there to indicate if recompression might be worthwhile.

“OK”使用2,“bad”使用3.因此,差异本身不是问题。

为了进一步了解,您可以考虑为每个压缩和(尝试)解压缩提供以下信息:什么平台,什么版本的Python,什么版本的zlib库,用于调用zlib的实际代码是什么模块。还提供失败的解压缩尝试的完整回溯和错误消息。您是否尝试使用任何其他zlib阅读软件解压缩失败的文件?结果如何?请澄清你必须要做的事情:“我是否被冲洗?”是说您无权访问原始数据?它是如何从流到文件的?您有什么保证数据在传输中没有损坏?

更新基于部分澄清的一些观察结果发表在您的自我回答中:

您使用的是Windows。 Windows在读取和写入文件时区分二进制模式和文本模式。在文本模式下阅读时,Python 2.x将'\ r \ n'更改为'\ n',并在写入时将'\ n'更改为'\ r \ n'。处理非文本数据时,这不是一个好主意。更糟糕的是,在文本模式下阅读时,'\ x1a'也称为Ctrl-Z被视为文件结尾。

压缩文件:

# imports and other superstructure left as a exercise
str_object1 = open('my_log_file', 'rb').read()
str_object2 = zlib.compress(str_object1, 9)
f = open('compressed_file', 'wb')
f.write(str_object2)
f.close()

解压缩文件:

str_object1 = open('compressed_file', 'rb').read()
str_object2 = zlib.decompress(str_object1)
f = open('my_recovered_log_file', 'wb')
f.write(str_object2)
f.close()

除此之外:最好使用gzip模块,这样你就不必考虑像文本模式这样的nasssties,只需花费几个字节来获取额外的标题信息。

如果您在压缩代码中使用'rb'和'wb'但在解压缩代码中没有[不太可能?],那么您就不需要加油,只需要充实上面的解压缩代码就可以了。

请注意以下未经测试的提示中使用“may”,“should”等。

如果您在压缩代码中没有使用'rb'和'wb',那么您自己吸收的概率相当高。

如果您的原始文件中存在'\ x1a'的任何实例,则第一个之后的任何数据都会丢失 - 但在这种情况下,它不应该在解压缩时失败(IOW此方案与您的症状不符)

如果zlib本身生成了Ctrl-Z,这应该会在尝试解压缩时导致早期EOF,这当然会导致异常。在这种情况下,您可以通过以二进制模式读取压缩文件来小心地反转该过程,然后将'\ r \ n'替换为'\ n'[即在没有Ctrl-Z的情况下模拟文本模式 - > EOF噱头]。解压缩结果。 编辑以TEXT模式写出结果。 结束修改

更新2 我可以使用以下脚本重现您的症状 - 任何级别1到9 -

import zlib, sys
fn = sys.argv[1]
level = int(sys.argv[2])
s1 = open(fn).read() # TEXT mode
s2 = zlib.compress(s1, level)
f = open(fn + '-ct', 'w') # TEXT mode
f.write(s2)
f.close()
# try to decompress in text mode
s1 = open(fn + '-ct').read() # TEXT mode
s2 = zlib.decompress(s1) # error -5
f = open(fn + '-dtt', 'w')
f.write(s2)
f.close()

注意:您需要使用一个相当大的文本文件(我使用80kb源文件)以确保解压缩结果包含'\ x1a'。

我可以使用此脚本恢复:

import zlib, sys
fn = sys.argv[1]
# (1) reverse the text-mode write
# can't use text-mode read as it will stop at Ctrl-Z
s1 = open(fn, 'rb').read() # BINARY mode
s1 = s1.replace('\r\n', '\n')
# (2) reverse the compression
s2 = zlib.decompress(s1)
# (3) reverse the text mode read
f = open(fn + '-fixed', 'w') # TEXT mode
f.write(s2)
f.close()

注意:如果原始文件中存在'\ x1a'又名Ctrl-Z字节,并且文本模式下读取该文件,则该字节和所有后续字节将不包含在压缩文件中,因此无法恢复。对于文本文件(例如源代码),这根本不是任何损失。对于二进制文件,您最有可能被软管。

更新3 [之后发现问题涉及加密/解密层]:

“错误-5”消息表示您尝试解压缩的数据因压缩而被破坏。如果它不是由文件上的文本模式引起的,那么显然(?)的怀疑就会落在你的解密和加密包装上。如果您需要帮助,则需要泄露这些包装器的来源。实际上你应该尝试做的是(像我一样)组合一个小脚本,在多个输入文件上重现问题。其次(就像我一样)看看你是否可以在什么条件下扭转这个过程。如果您需要第二阶段的帮助,则需要泄露问题复制脚本。

答案 1 :(得分:3)

我在寻找

python -c 'import sys,zlib;sys.stdout.write(zlib.decompress(sys.stdin.read()))'

自己写的;根据{{​​3}}

的答案

答案 2 :(得分:0)

好的抱歉,我不够清楚。这是win32,python 2.6.2。我担心我找不到zlib文件,但它包含在win32二进制版本中。而且我无法访问原始数据 - 我一直在压缩我的日志文件,我想让它们恢复原状。至于其他软件,我已经尝试了7zip,但当然它失败了,因为它是zlib,而不是gzip(我无法直接解压缩zlib流的任何软件)。我现在无法给出回溯的副本,但它(追溯到zlib.decompress(data))zlib.error:错误:-3。另外,要清楚的是,这些是静态文件,而不是流,因为我让它听起来更早(因此没有传输错误)。而且我害怕我没有代码,但我知道我使用了zlib.compress(data,9)(即在最高压缩级别 - 尽管,有趣的是,似乎并非所有zlib输出都是78da as你可能会期待,因为我把它放在最高级别)并且只是zlib.decompress()。

答案 3 :(得分:0)

对不起我的上一篇文章,我没有掌握一切。我无法编辑我的帖子,因为我没有使用OpenID。无论如何,这里有一些数据:

1)减压追溯:

Traceback (most recent call last):
  File "<my file>", line 5, in <module>
    zlib.decompress(data)
zlib.error: Error -5 while decompressing data

2)压缩代码:

#here you can assume the data is the data to be compressed/stored
data = encrypt(zlib.compress(data,9)) #a short wrapper around PyCrypto AES encryption
f = open("somefile", 'wb')
f.write(data)
f.close()

3)解压缩代码:

f = open("somefile", 'rb')
data = f.read()
f.close()

zlib.decompress(decrypt(data)) #this yeilds the error in (1)