29 votes

Python : Implémentations de Inflate et Deflate

Je suis en interface avec un serveur qui exige que les données qui lui sont envoyées soient compressées avec Dégonfler (codage Huffman + LZ77) et envoie également des données que je dois Gonfler .

Je sais que Python inclut Zlib, et que les bibliothèques C de Zlib prennent en charge les appels à Gonfler y Dégonfler mais ces éléments ne sont apparemment pas fournis par le module Zlib de Python. Il fournit Compresser y Décompression mais lorsque je passe un appel tel que celui-ci :

result_data = zlib.decompress( base64_decoded_compressed_string )

Je reçois l'erreur suivante :

Error -3 while decompressing data: incorrect header check

Gzip ne fait pas mieux ; lors d'un appel tel que :

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

Je reçois l'erreur :

IOError: Not a gzipped file

ce qui est logique puisque les données sont un Dégonflé pas un vrai fichier Gzippé archivo.

Maintenant je sais qu'il y a un Dégonfler disponible (Pyflate), mais je ne connais pas d'implémentation de la Gonfler mise en œuvre.

Il semble qu'il y ait quelques options :

  1. Trouver une mise en œuvre existante (idéale) de Gonfler y Dégonfler en Python
  2. Écrire ma propre extension Python à la bibliothèque c zlib qui inclut Gonfler y Dégonfler
  3. Appelez quelque chose d'autre qui peut être exécuté à partir de la ligne de commande (comme un script de Ruby, car Gonfler / Dégonfler dans zlib sont entièrement enveloppés dans Ruby)
  4. ?

Je suis à la recherche d'une solution, mais en l'absence de solution, je serai reconnaissant pour les idées, les opinions constructives et les idées.

Informations complémentaires : Le résultat de la déflation (et du codage) d'une chaîne de caractères devrait, pour les besoins dont j'ai besoin, donner le même résultat que le bout de code C# suivant, où le paramètre d'entrée est un tableau d'octets UTF correspondant aux données à compresser :

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

L'exécution de ce code .NET pour la chaîne "deflate and encode me" donne le résultat suivant

7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8iZvl5mbV5mi1nab6cVrM8XeT/Dw==

Lorsque "deflate and encode me" est exécuté par la fonction Python Zlib.compress() puis encodé en base64, le résultat est "eJxLSU3LSSxJVUjMS1FIzUvOT0lVyE0FAFXHB6k=".

Il est clair que zlib.compress() n'est pas une implémentation du même algorithme que l'algorithme standard Deflate.

Plus d'informations :

Les 2 premiers octets des données deflate .NET ("7b0HY..."), après décodage b64 sont 0xEDBD, ce qui ne correspond pas aux données Gzip (0x1f8b), BZip2 (0x425A) ou Zlib (0x789C).

Les 2 premiers octets des données compressées Python ("eJxLS..."), après décodage b64 sont 0x789C. Il s'agit d'un en-tête Zlib.

SOLVED

Pour gérer le deflate et l'inflate brut, sans en-tête ni somme de contrôle, les choses suivantes devaient se produire :

En cas de déflation/compression : suppression des deux premiers octets (en-tête) et des quatre derniers octets (somme de contrôle).

Sur le gonflage/décompression : il y a un deuxième argument pour la taille de la fenêtre. Si cette valeur est négative, elle supprime les en-têtes. Voici mes méthodes actuelles, y compris l'encodage/décodage base64 - et elles fonctionnent correctement :

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 )

14voto

Markus Jarderot Points 33893

Vous pouvez toujours utiliser le zlib pour gonfler/dégonfler les données. Le site gzip l'utilise en interne, mais ajoute un en-tête de fichier pour le transformer en fichier gzip. En regardant le gzip.py quelque chose comme ça pourrait fonctionner :

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

Je ne sais pas si cela correspond exactement à ce que votre serveur exige, mais ces deux fonctions sont capables de faire le tour de toutes les données que j'ai essayées.

Les paramètres correspondent directement à ce qui est passé aux fonctions de la bibliothèque zlib.

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

Les constructeurs créent la structure et la remplissent avec des valeurs par défaut, puis la transmettent aux fonctions d'initialisation. Le site compress / decompress mettent à jour la structure et la transmettent à inflate / deflate .

13voto

John Machin Points 39706

Il s'agit d'un complément à la réponse de MizardX, qui fournit quelques explications et un historique.

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

Según RFC 1950 un flux zlib construit de la manière par défaut est composé de :

  • un en-tête de 2 octets (par exemple, 0x78 0x9C)
  • un flux deflate -- voir RFC 1951
  • une somme de contrôle Adler-32 des données non comprimées (4 octets)

Le C# DeflateStream fonctionne sur (vous l'avez deviné) un flux deflate. Le code de MizardX dit au module zlib que les données sont un flux deflate brut.

Observations : (1) On espère que la méthode C# "deflation" produisant une chaîne plus longue ne se produit qu'avec des entrées courtes (2) Utiliser le flux deflate brut sans la somme de contrôle Adler-32 ? Un peu risqué, à moins de le remplacer par quelque chose de mieux.

Mises à jour

message d'erreur Block length does not match with its complement

Si vous essayez de gonfler des données compressées avec le programme C# DeflateStream et que vous obtenez ce message, alors il est tout à fait possible que vous lui donniez un flux zlib, et non un flux deflate.

Voir Comment utiliser un DeflateStream sur une partie d'un fichier ?

Copiez/collez également le message d'erreur dans une recherche Google et vous obtiendrez de nombreuses réponses (y compris celle qui figure au début de cette réponse) disant à peu près la même chose.

Le Java Deflater ... utilisé par "le site web" ... C# DeflateStream "est assez simple et a été testé contre l'implémentation Java". Parmi les constructeurs de Deflater Java suivants, lequel le site Web utilise-t-il ?

public Deflater(int level, boolean nowrap)

Crée un nouveau compresseur en utilisant le niveau de compression spécifié. Si 'nowrap' est vrai, les champs d'en-tête et de somme de contrôle ZLIB ne seront pas utilisés afin de prendre en charge le format de compression utilisé dans GZIP et PKZIP.

public Deflater(int level)

Crée un nouveau compresseur en utilisant le niveau de compression spécifié. Les données comprimées seront générées au format ZLIB.

public Deflater()

Crée un nouveau compresseur avec le niveau de compression par défaut. Les données comprimées seront générées au format ZLIB.

Un dégonfleur en une ligne après avoir jeté l'en-tête zlib de 2 octets et la somme de contrôle de 4 octets :

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

o

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X