2009-07-06 35 views
40

Tôi đang giao tiếp với máy chủ yêu cầu dữ liệu được gửi đến đó được nén với Làm lệch hướng thuật toán (Huffman encoding + LZ77) và cũng gửi dữ liệu mà tôi cần đến Inflate.Python: Thổi phồng và làm lệch triển khai

Tôi biết rằng Python bao gồm Zlib, và rằng các thư viện C trong zlib cuộc gọi hỗ trợ để BơmDeflate, nhưng những hình như không được cung cấp bởi module Python zlib. Nó cung cấp CompressDecompress, nhưng khi tôi thực hiện cuộc gọi như sau:

result_data = zlib.decompress(base64_decoded_compressed_string) 

tôi nhận được lỗi sau:

Error -3 while decompressing data: incorrect header check 

Gzip làm không tốt; khi thực hiện cuộc gọi như:

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

tôi nhận được lỗi:

IOError: Not a gzipped file 

mà làm cho cảm giác như là dữ liệu là một xì hơi tập tin không phải là một sự thật Gzipped tập tin.

Bây giờ tôi biết rằng có Làm lệch hoạt động sẵn có (Pyflate), nhưng tôi không biết về việc triển khai Inflate.

Dường như có một vài lựa chọn:

  1. Tìm một thực hiện có (lý tưởng) của BơmDeflate bằng Python
  2. Viết phần mở rộng Python của riêng tôi với zlib c thư viện bao gồm Thổi phồngLàm lệch
  3. Gọi một thứ khác có thể là được thực hiện từ dòng lệnh (chẳng hạn như tập lệnh Ruby, kể từ Thổi phồng/Làm lệch cuộc gọi trong zlib được gói đầy đủ trong Ruby)
  4. ?

Tôi đang tìm kiếm giải pháp, nhưng thiếu giải pháp tôi sẽ biết ơn vì thông tin chi tiết, ý kiến ​​xây dựng và ý tưởng.

thông tin bổ sung: Kết quả của việc xì hơi (và mã hóa) một chuỗi nên, cho các mục đích tôi cần, cung cấp cho các kết quả tương tự như đoạn sau đây của mã C#, nơi mà các tham số đầu vào là một mảng byte UTF tương ứng với dữ liệu cần nén:

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

Chạy ứng dụng này.NET mã cho chuỗi "deflate và mã hóa tôi" cho kết quả

7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8iZvl5mbV5mi1nab6cVrM8XeT/Dw== 

Khi "deflate và mã hóa cho tôi" được chạy qua Python Zlib.compress() và sau đó mã hóa base64, kết quả là "eJxLSU3LSSxJVUjMS1FIzUvOT0lVyE0FAFXHB6k =" .

Rõ ràng là zlib.compress() không phải là việc triển khai cùng một thuật toán với thuật toán Deflate chuẩn.

biết thêm thông tin:

2 byte đầu tiên của NET deflate dữ liệu ("7b0HY ..."), sau khi giải mã b64 là 0xEDBD, mà không tương ứng với dữ liệu Gzip (0x1f8b), BZip2 (0x425A) dữ liệu hoặc dữ liệu Zlib (0x789C).

2 byte đầu tiên của dữ liệu được nén Python ("eJxLS ..."), sau khi giải mã b64 là 0x789C. Đây là tiêu đề Zlib.

SOLVED

Để xử lý các deflate liệu và thổi phồng, không header và tổng kiểm tra, những điều sau đây cần thiết để xảy ra:

On deflate/nén: dải hai byte đầu tiên (header) và bốn byte cuối cùng (tổng kiểm tra).

Khi thổi phồng/giải nén: có đối số thứ hai cho kích thước cửa sổ. Nếu giá trị này là âm, nó sẽ chặn tiêu đề. đây là phương pháp của tôi hiện nay, bao gồm cả mã hóa base64/giải mã - và hoạt động bình thường:

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) 

Trả lời

16

Đây là phần bổ trợ cho câu trả lời của MizardX, đưa ra một số giải thích và nền.

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

Theo RFC 1950, một dòng zlib xây dựng theo cách mặc định bao gồm:

  • một tiêu đề 2-byte (ví dụ 0x78 0x9C)
  • một dòng deflate - xem RFC 1951
  • tổng kiểm tra Adler-32 của dữ liệu chưa nén (4 byte)

C# DeflateStream hoạt động trên (bạn đoán nó) một luồng làm lệch hướng. Mã của MizardX nói với mô-đun zlib rằng dữ liệu là luồng làm lệch nguyên.

Quan sát: (1) Người ta hy vọng phương pháp "giảm phát" C# tạo ra chuỗi dài hơn chỉ xảy ra với đầu vào ngắn (2) Sử dụng luồng làm lệch nguyên không có tổng kiểm tra Adler-32? Bit nguy hiểm, trừ khi thay thế bằng một cái gì đó tốt hơn.

cập nhật

thông báo lỗi Block length does not match with its complement

Nếu bạn đang cố gắng để thổi phồng một số dữ liệu nén với C# DeflateStream và bạn nhận được tin nhắn đó, sau đó nó là hoàn toàn có thể rằng bạn đang cho nó aa luồng zlib, không phải là luồng xì hơi.

Xem How do you use a DeflateStream on part of a file?

Cũng sao chép/dán thông báo lỗi vào một tìm kiếm Google và bạn sẽ nhận được rất nhiều hits (bao gồm cả lên mặt trước của câu trả lời này) nói nhiều điều tương tự.

Java Deflater ... được sử dụng bởi "trang web" ... C# DeflateStream "khá đơn giản và đã được thử nghiệm chống lại việc triển khai Java". Nhà xây dựng Java Deflater nào sau đây có thể sử dụng trang web nào?

public Deflater(int level, boolean nowrap)

Creates a new compressor using the specified compression level. If 'nowrap' is true then the ZLIB header and checksum fields will not be used in order to support the compression format used in both GZIP and PKZIP.

public Deflater(int level)

Creates a new compressor using the specified compression level. Compressed data will be generated in ZLIB format.

public Deflater()

Creates a new compressor with the default compression level. Compressed data will be generated in ZLIB format.

Một một dòng deflater sau khi ném đi header zlib 2-byte và checksum 4-byte:

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

hoặc

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

+1 Cảm ơn thông tin bổ sung. – Demi

+0

@ John Machin: Để trả lời quan sát đầu tiên của bạn ... kết quả chỉ dài hơn trong trường hợp các chuỗi ngắn hơn (header? Padding?). Khi tôi ăn trong 161 byte dữ liệu để giảm phát, trước khi mã hóa base64 kết quả là 126 byte. – Demi

+0

@ John Machin: Thông tin chi tiết và thông tin tuyệt vời. Chữ ký Java của deflater được sử dụng là chữ cái có hai tham số, với nowrap == true. Tôi đã sử dụng ví dụ deflater một dòng của bạn và nó thổi phồng tốt trong .NET và Java, mặc dù có vẻ khác so với giá trị được tạo ra bởi deflating với các thư viện trong các ngôn ngữ đó. Điều đó thật tuyệt. Bây giờ tôi đang làm việc trên thổi phồng - lấy dữ liệu xì hơi được tạo ra bởi Java hoặc .NET và thêm vào một adler32 checksum và tiêu đề zlib để xem liệu tôi có thể lấy Python để tiêu thụ nó tốt hay không. Tôi sẽ cho bạn biết làm thế nào nó đi. – Demi

17

Bạn vẫn có thể sử dụng các mô-đun zlib để tăng/deflate dữ liệu. Mô-đun gzip sử dụng nó trong nội bộ, nhưng thêm tiêu đề tệp để biến nó thành tệp gzip. Nhìn vào các tập tin gzip.py, một cái gì đó như thế này có thể làm việc:

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 

Tôi không biết nếu điều này tương ứng chính xác với những gì máy chủ của bạn yêu cầu, nhưng những hai chức năng có thể khứ hồi bất kỳ dữ liệu tôi đã cố gắng.

Các tham số ánh xạ trực tiếp đến những gì được chuyển đến các hàm thư viện zlib.

PythonC
zlib.compressobj(...)deflateInit(...)
compressobj.compress(...)deflate(...)
zlib.decompressobj(...)inflateInit(...)
decompressobj.decompress(...)inflate(...)

Các hàm tạo tạo cấu trúc và điền nó với các giá trị mặc định và chuyển nó đến các hàm init. Phương thức compress/decompress cập nhật cấu trúc và chuyển nó đến inflate/deflate.

+0

Những gì tôi đang tìm kiếm cho là truy cập vào các C-cấp Inflate và Deflate cuộc gọi của thư viện mà các mô-đun Zlib Python kết thúc tốt đẹp. Nó không xuất hiện rằng giải nén và nén làm điều tương tự, và mô-đun Zlib Python không phơi bày Inflate và Deflate – Demi

+0

Điều này là không hữu ích. Xin lưu ý thông tin bổ sung tôi đã thêm vào câu hỏi của mình ở trên. Mã bạn cung cấp ở trên, khi chạy với chuỗi "xì hơi và mã hóa tôi", kết quả là "S0lNy0ksSVVIzEtRSM1Lzk9JVchNBQA =", thậm chí còn ngắn hơn. Kết quả Deflate chính xác sẽ giống như chuỗi được tạo ra (dài hơn) .NET mà tôi lưu ý ở trên. – Demi

+0

Chuỗi đầu vào 21 ký tự dẫn đến kết quả đầu ra là 212 byte bị xì hơi như thế nào? Điều đó bao gồm một tiêu đề tập tin deflate? –

Các vấn đề liên quan