Comment puis-je décompresser un flux gzip avec zlib?

108

Les fichiers au format Gzip (créés avec le gzipprogramme, par exemple) utilisent l'algorithme de compression "deflate", qui est le même algorithme de compression que celui utilisé par zlib . Cependant, lorsque vous utilisez zlib pour gonfler un fichier compressé gzip, la bibliothèque renvoie un fichier Z_DATA_ERROR.

Comment puis-je utiliser zlib pour décompresser un fichier gzip?

Greg Hewgill
la source

Réponses:

118

Pour décompresser un fichier au format gzip avec zlib, appelez inflateInit2avec le windowBitsparamètre as 16+MAX_WBITS, comme ceci:

inflateInit2(&stream, 16+MAX_WBITS);

Si vous ne le faites pas, zlib se plaindra d'un mauvais format de flux. Par défaut, zlib crée des flux avec un en-tête zlib, et sur inflate ne reconnaît pas les différents en-têtes gzip sauf si vous le lui dites. Bien que cela soit documenté à partir de la version 1.2.1 du zlib.hfichier d'en-tête, ce n'est pas dans le manuel zlib . Depuis le fichier d'en-tête:

windowBitspeut également être supérieur à 15 pour le décodage gzip facultatif. Ajoutez 32 à windowBitspour activer le décodage zlib et gzip avec détection automatique des en-têtes, ou ajoutez 16 pour décoder uniquement le format gzip (le format zlib renverra a Z_DATA_ERROR). Si un flux gzip est en cours de décodage, strm->adlerest un crc32 au lieu d'un adler32.

Greg Hewgill
la source
35
En python:zlib.decompress(data, 15 + 32)
Roman Starkov
3
Merci, c'était très frustrant jusqu'à ce que je trouve ce message.
Alex le
Wow, c'est la question de 2009. Merci @Greg Hewgill
YuAn Shaolin Maculelê Lai
Peut-être pouvez-vous fournir quelques directives pour la décompression itérative du flux gzip. Dans la décompression gzip one-shot où votre flux de sortie et votre taille doivent être fixes et suffisants pour stocker toute la sortie décompressée. Cette valeur dépend de l'efficacité de la décompression gzip qui peut varier en fonction de l'entropie des données. Existe-t-il un moyen d'allouer dynamiquement plus d'espace au tampon de sortie en cas de besoin? Merci
Zohar81
104

python

zlibla bibliothèque prend en charge :

Le zlibmodule python les supportera également.

choisir windowBits

Mais zlibpeut décompresser tous ces formats:

  • pour (dé-) compresser le deflateformat, utilisezwbits = -zlib.MAX_WBITS
  • pour (dé-) compresser le zlibformat, utilisezwbits = zlib.MAX_WBITS
  • pour (dé-) compresser le gzipformat, utilisezwbits = zlib.MAX_WBITS | 16

Voir la documentation sur http://www.zlib.net/manual.html#Advanced (section inflateInit2)

exemples

données de test:

>>> deflate_compress = zlib.compressobj(9, zlib.DEFLATED, -zlib.MAX_WBITS)
>>> zlib_compress = zlib.compressobj(9, zlib.DEFLATED, zlib.MAX_WBITS)
>>> gzip_compress = zlib.compressobj(9, zlib.DEFLATED, zlib.MAX_WBITS | 16)
>>> 
>>> text = '''test'''
>>> deflate_data = deflate_compress.compress(text) + deflate_compress.flush()
>>> zlib_data = zlib_compress.compress(text) + zlib_compress.flush()
>>> gzip_data = gzip_compress.compress(text) + gzip_compress.flush()
>>> 

test évident pour zlib:

>>> zlib.decompress(zlib_data)
'test'

tester pour deflate:

>>> zlib.decompress(deflate_data)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
zlib.error: Error -3 while decompressing data: incorrect header check
>>> zlib.decompress(deflate_data, -zlib.MAX_WBITS)
'test'

tester pour gzip:

>>> zlib.decompress(gzip_data)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
zlib.error: Error -3 while decompressing data: incorrect header check
>>> zlib.decompress(gzip_data, zlib.MAX_WBITS|16)
'test'

les données sont également compatibles avec le gzipmodule:

>>> import gzip
>>> import StringIO
>>> fio = StringIO.StringIO(gzip_data)
>>> f = gzip.GzipFile(fileobj=fio)
>>> f.read()
'test'
>>> f.close()

détection automatique des en-têtes (zlib ou gzip)

l'ajout 32à windowBitsdéclenchera la détection d'en-tête

>>> zlib.decompress(gzip_data, zlib.MAX_WBITS|32)
'test'
>>> zlib.decompress(zlib_data, zlib.MAX_WBITS|32)
'test'

utiliser à la gzipplace

Pour les gzipdonnées avec en-tête gzip, vous pouvez utiliser gzipdirectement le module; mais n'oubliez pas que sous le capot , gziputilise zlib.

fh = gzip.open('abc.gz', 'rb')
cdata = fh.read()
fh.close()
Dnozay
la source
3
pourquoi cette pièce d'or n'est pas sur la documentation sur ce format exactement?
Ramon Moraes
N'hésitez pas à envoyer une pull request / patch contre cpython en utilisant l'une de ces réponses.
dnozay
excellente réponse pour les chaînes, une idée comment faire cela pour un flux sans lire le fichier entier en mémoire?
Josh J
Je vous remercie. Je peux résoudre mon problème de décompression dans mon code source avec votre réponse.
Bethlee
incroyable, c'est une pépite d'or .. mais je ne peux pas m'empêcher de penser que ce sont des «nombres magiques»? où est-ce mentionné dans la documentation? J'ai regardé, mais je dois vraiment pas avoir vérifié assez dur .. aussi, la notation que je ne suis pas entièrement. Que signifie le | signifie, est-ce facultatif? et pourquoi le dégonflage est négatif .. MAX_WBITS est-il une constante .. 🙁
m1nkeh
3

La structure de zlib et gzip est différente. zlib utilise la RFC 1950 et gzip utilise la RFC 1952 , donc ont des en-têtes différents mais le reste a la même structure et suit la RFC 1951 .

josep fon
la source