Python:膨胀和放气的实现

我与一个服务器连接,要求发送给它的数据使用Deflatealgorithm(Huffman编码+ LZ77)进行压缩,并且还将需要的数据发送给Inflate

我知道Python包含Zlib,并且Zlib中的C库支持调用InflateDeflate ,但这些显然不是由Python Zlib模块提供的。 它确实提供了压缩 和解压缩 ,但是当我打电话如下:

result_data = zlib.decompress( base64_decoded_compressed_string ) 

我收到以下错误:

 Error -3 while decompressing data: incorrect header check 

Gzip没有更好的; 在打电话时如:

 result_data = gzip.GzipFile( fileobj = StringIO.StringIO( base64_decoded_compressed_string ) ).read() 

我收到错误:

 IOError: Not a gzipped file 

这是有道理的,因为数据是一个压缩文件而不是一个真正的Gzipped文件。

现在我知道有一个Deflate实现可用(Pyflate),但我不知道Inflate实现。

看起来有几个select:

  1. 在Python中查找现有的充气 实施(理想)
  2. 写我自己的Python扩展到zlib c库,其中包括充气和放
  3. 调用可以从命令行执行的其他内容(例如Ruby脚本,因为zlib中的“ 充气 /放气”调用完全包装在Ruby中)

我正在寻求解决scheme,但缺乏解决scheme,我会感谢见解,build设性意见和想法。

附加信息 :为了我需要的目的,缩减(和编码)string的结果应该与下面的C#代码片段给出相同的结果,其中input参数是与要压缩的数据对应的UTF字节数组:

 public static string DeflateAndEncodeBase64(byte[] data) { if (null == data || data.Length < 1) return null; string compressedBase64 = ""; //write into a new memory stream wrapped by a deflate stream using (MemoryStream ms = new MemoryStream()) { using (DeflateStream deflateStream = new DeflateStream(ms, CompressionMode.Compress, true)) { //write byte buffer into memorystream deflateStream.Write(data, 0, data.Length); deflateStream.Close(); //rewind memory stream and write to base 64 string byte[] compressedBytes = new byte[ms.Length]; ms.Seek(0, SeekOrigin.Begin); ms.Read(compressedBytes, 0, (int)ms.Length); compressedBase64 = Convert.ToBase64String(compressedBytes); } } return compressedBase64; } 

运行这个.NET代码的string“deflate和编码我”给出了结果

 7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8iZvl5mbV5mi1nab6cVrM8XeT/Dw== 

当“放气和编码我”通过Python Zlib.compress()运行,然后base64编码,结果是“eJxLSU3LSSxJVUjMS1FIzUvOT0lVyE0FAFXHB6k =”。

很明显,zlib.compress()不是与标准的Deflatealgorithm相同的algorithm的实现。

更多信息

.NET解压缩数据的前两个字节(“7b0HY …”)在b64解码之后是0xEDBD,它不对应于Gzip数据(0x1f8b),BZip2(0x425A)数据或Zlib(0x789C)数据。

Python压缩数据的前两个字节(“eJxLS …”)在b64解码之后是0x789C。 这是一个Zlib标题。

解决了

为了处理原始的deflate和膨胀,没有头和校验和,需要发生以下事情:

在压缩/压缩:剥离前两个字节(标题)和最后四个字节(校验和)。

在膨胀/解压缩:窗口大小有第二个参数。 如果这个值是负值,它会压缩标题。 这里是我目前的方法,包括base64编码/解码 – 并正常工作:

 import zlib import base64 def decode_base64_and_inflate( b64string ): decoded_data = base64.b64decode( b64string ) return zlib.decompress( decoded_data , -15) def deflate_and_base64_encode( string_val ): zlibbed_str = zlib.compress( string_val ) compressed_string = zlibbed_str[2:-4] return base64.b64encode( compressed_string ) 

这是MizardX的答案附加,给出一些解释和背景。

http://www.chiramattel.com/george/blog/2007/09/09/deflatestream-block-length-does-not-match.html

根据RFC 1950 ,以默认方式构build的zlibstream包括:

  • 一个2字节的头(例如0x78 0x9C)
  • 一个deflatestream – 见RFC 1951
  • 未压缩数据的Adler-32校验和(4字节)

C# DeflateStream工作(你猜对了)一个deflatestream。 MizardX的代码告诉zlib模块数据是一个原始的deflatestream。

观察:(1)希望C#“放气”方法产生一个更长的string只发生短input(2)使用没有Adler-32校验和的原始deflatestream? 有点危险,除非用更好的东西代替。

更新

错误信息Block length does not match with its complement

如果你想用C# DeflateStream来压缩一些压缩的数据,并且你得到了这个消息,那么你很可能会给它一个zlibstream而不是deflatestream。

请参阅如何在文件的一部分上使用DeflateStream?

也复制/粘贴错误信息到谷歌search,你会得到许多命中(包括这个答案的前面)说同样的事情。

Java Deflater …用于“网站”… C#DeflateStream“非常简单,并且已经通过Java实现进行了testing”。 网站使用了以下哪些可能的Java Deflater构造函数?

public Deflater(int level, boolean nowrap)

使用指定的压缩级别创build一个新的压缩器。 如果'nowrap'为true,那么将不会使用ZLIB头和校验和字段,以支持GZIP和PKZIP中使用的压缩格式。

public Deflater(int level)

使用指定的压缩级别创build一个新的压缩器。 压缩数据将以ZLIB格式生成。

public Deflater()

用默认的压缩级别创build一个新的压缩器。 压缩数据将以ZLIB格式生成。

扔掉2字节zlib标题和4字节校验和之后,

 uncompressed_string.encode('zlib')[2:-4] # does not work in Python 3.x 

要么

 zlib.compress(uncompressed_string)[2:-4] 

您仍然可以使用zlib模块来充气/放气数据。 gzip模块在内部使用它,但添加了一个文件头,使其成为一个gzip文件。 看着gzip.py文件,这样的事情可以工作:

 import zlib def deflate(data, compresslevel=9): compress = zlib.compressobj( compresslevel, # level: 0-9 zlib.DEFLATED, # method: must be DEFLATED -zlib.MAX_WBITS, # window size in bits: # -15..-8: negate, suppress header # 8..15: normal # 16..30: subtract 16, gzip header zlib.DEF_MEM_LEVEL, # mem level: 1..8/9 0 # strategy: # 0 = Z_DEFAULT_STRATEGY # 1 = Z_FILTERED # 2 = Z_HUFFMAN_ONLY # 3 = Z_RLE # 4 = Z_FIXED ) deflated = compress.compress(data) deflated += compress.flush() return deflated def inflate(data): decompress = zlib.decompressobj( -zlib.MAX_WBITS # see above ) inflated = decompress.decompress(data) inflated += decompress.flush() return inflated 

我不知道这是否与您的服务器要求相符,但是这两个函数能够对我尝试的任何数据进行往返。

参数直接映射到传递给zlib库函数的内容。

Python⇒C
zlib.compressobj(...) deflateInit(...)
compressobj.compress(...)deflate(...)
zlib.decompressobj(...) inflateInit(...)
decompressobj.decompress(...)inflate(...)

构造函数创build结构并使用默认值填充它,并将其传递给init函数。 compress / decompress方法更新结构并将其传递给inflate / deflate