web-dev-qa-db-ja.com

Python:InflateおよびDeflateの実装

私はそれに送信されるデータがDeflateアルゴリズム(ハフマンエンコーディング+ LZ77)で圧縮され、必要なデータを送信する膨らます

PythonはZlibを含み、ZlibのCライブラリはInflateおよびDeflate、しかし、これらは明らかにPython Zlibモジュールによって提供されません。それはCompressおよびDecompressを提供しますが、次のような電話をかけると:

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

データはDeflatedファイルであり、真のGzip圧縮ファイルではないため、これは理にかなっています。

現在、Deflate実装(Pyflate)があることはわかっていますが、Inflate実装はわかりません。

いくつかのオプションがあるようです:

  1. PythonでInflateおよびDeflateの既存の実装(理想)を見つける
  2. 独自のPythonInflateおよびDeflateを含むzlib cライブラリの拡張機能を記述します。
  3. コマンドラインから実行できる他の何かを呼び出します(Rubyスクリプト、Inflate/Deflateなど) zlibの呼び出しは完全にRubyでラップされます)

私は解決策を探していますが、解決策が欠けているので、洞察、建設的な意見、およびアイデアに感謝します。

追加情報:文字列のデフレート(およびエンコード)の結果は、必要な目的のために、入力パラメーターがUTFバイトの配列である次のC#コードのスニペットと同じ結果になるはずです。圧縮するデータに対応:

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;
}

文字列「deflate and encode me」に対してこの.NETコードを実行すると、結果が得られます

7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8iZvl5mbV5mi1nab6cVrM8XeT/Dw==

「deflate and encode me」をPython Zlib.compress()で実行し、base64でエンコードすると、結果は「eJxLSU3LSSxJVUjMS1FIzUvOT0lVyE0FAFXHB6k = "」になります。

Zlib.compress()が標準のDeflateアルゴリズムと同じアルゴリズムの実装ではないことは明らかです。

詳細

B64デコード後の.NET deflateデータ( "7b0HY ...")の最初の2バイトは0xEDBDであり、Gzipデータ(0x1f8b)、BZip2(0x425A)データ、またはZlib(0x789C)データに対応しません。

Python圧縮データ( "eJxLS ...")の最初の2バイトは、b64デコード後は0x789Cです。これはZlibヘッダーです。

[〜#〜] solved [〜#〜]

ヘッダーとチェックサムを使用せずに生のデフレートとインフレートを処理するには、次のことが必要です。

Deflate/compressの場合:最初の2バイト(ヘッダー)と最後の4バイト(チェックサム)を取り除きます。

Inflate/decompressの場合:ウィンドウサイズの2番目の引数があります。この値が負の場合、ヘッダーが抑制されます。現在、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 )
51
Demi

これはMizardXの答えのアドオンであり、いくつかの説明と背景を提供します。

http://www.chiramattel.com/george/blog/2007/09/09/deflatestream-block-length-does-not-match.html を参照してください

RFC 195 によると、デフォルトの方法で構築されたzlibストリームは次のもので構成されます。

  • 2バイトのヘッダー(例:0x78 0x9C)
  • デフレートストリーム- RFC 1951 を参照
  • 非圧縮データのAdler-32チェックサム(4バイト)

C#DeflateStreamはdeflateストリームで動作します(ご想像のとおり)。 MizardXのコードは、データが生のデフレートストリームであることをzlibモジュールに伝えています。

観察:(1)より長い文字列を生成するC#の「デフレーション」メソッドが短い入力でのみ起こることを望みます(2)Adler-32チェックサムなしで生のデフレートストリームを使用しますか?より良いものに置き換えない限り、少し危険です。

更新

エラーメッセージ_Block length does not match with its complement_

C#DeflateStreamを使用して圧縮データを膨張させようとしていて、そのメッセージが表示された場合、deflateストリームではなくzlibストリームを提供している可能性は十分にあります。

ファイルの一部でDeflateStreamをどのように使用しますか? を参照してください

また、エラーメッセージをGoogle検索にコピーして貼り付けると、ほぼ同じことを言っているヒット(この回答の先頭にあるものを含む)が表示されます。

Java Deflater...「ウェブサイトで使用"... C#DeflateStream"は非常に簡単で、Java実装]に対してテストされています。次の可能なJava Deflaterコンストラクターは、 ?

public Deflater(int level, boolean nowrap)

指定された圧縮レベルを使用して新しいコンプレッサーを作成します。 「nowrap」がtrueの場合、GZIPとPKZIPの両方で使用される圧縮形式をサポートするために、ZLIBヘッダーとチェックサムフィールドは使用されません。

public Deflater(int level)

指定された圧縮レベルを使用して新しいコンプレッサーを作成します。圧縮データはZLIB形式で生成されます。

public Deflater()

デフォルトの圧縮レベルで新しいコンプレッサーを作成します。圧縮データはZLIB形式で生成されます。

2バイトのzlibヘッダーと4バイトのチェックサムを捨てた後の1行のデフレーター

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

または

_zlib.compress(uncompressed_string)[2:-4]
_
21
John Machin

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
_

これがサーバーが必要とするものに完全に対応するかどうかはわかりませんが、これらの2つの関数は、私が試したデータをラウンドトリップできます。

パラメーターは、zlibライブラリー関数に渡されるものに直接マップされます。

Python[〜#〜] c [〜#〜]
zlib.compressobj(...)deflateInit(...)
compressobj.compress(...)deflate(...)
zlib.decompressobj(...)inflateInit(...)
decompressobj.decompress(...)inflate(...)

コンストラクターは構造を作成し、デフォルト値を設定して、init-functionsに渡します。 compress/decompressメソッドは構造を更新し、inflate/deflateに渡します。

21
Markus Jarderot