Jak mogę zdekompresować strumień gzip za pomocą zlib?


108

Pliki w formacie Gzip (utworzone gzipna przykład za pomocą programu) używają algorytmu kompresji „deflate”, który jest tym samym algorytmem kompresji, którego używa zlib . Jednak gdy używasz zlib do nadmuchania skompresowanego pliku gzip, biblioteka zwraca plik Z_DATA_ERROR.

Jak mogę użyć zlib do zdekompresowania pliku gzip?

Odpowiedzi:


118

Aby rozpakować plik formatu gzip z zlib, zadzwoń inflateInit2z windowBitsparametrem jak 16+MAX_WBITS, na przykład:

inflateInit2(&stream, 16+MAX_WBITS);

Jeśli tego nie zrobisz, zlib będzie narzekać na zły format strumienia. Domyślnie zlib tworzy strumienie z nagłówkiem zlib, a przy inflate nie rozpoznaje innego nagłówka gzip, chyba że tak powiesz. Chociaż jest to udokumentowane począwszy od wersji 1.2.1 zlib.hpliku nagłówkowego, nie ma tego w podręczniku zlib . Z pliku nagłówkowego:

windowBitsmoże być również większy niż 15 dla opcjonalnego dekodowania gzip. Dodaj 32 do, windowBitsaby włączyć dekodowanie zlib i gzip z automatycznym wykrywaniem nagłówka, lub dodaj 16, aby zdekodować tylko format gzip (format zlib zwróci a Z_DATA_ERROR). Jeśli dekodowany jest strumień gzip, strm->adlerjest to crc32 zamiast adler32.


35
W Pythonie:zlib.decompress(data, 15 + 32)
Roman Starkov

3
Dzięki, to było bardzo frustrujące, dopóki nie znalazłem tego posta.
Alex

Wow, to jest pytanie z 2009 roku. Dzięki @Greg Hewgill
YuAn Shaolin Maculelê Lai

Być może możesz podać kilka wskazówek dotyczących iteracyjnej dekompresji strumienia gzip. W jednorazowej dekompresji gzip, gdzie strumień wyjściowy i rozmiar powinny być stałe i wystarczające do przechowywania całego zdekompresowanego wyjścia. Ta wartość zależy od skuteczności dekompresji gzip, która może się zmieniać w zależności od entropii danych. Czy istnieje sposób, aby w razie potrzeby dynamicznie przydzielić więcej miejsca na bufor wyjściowy? Dzięki
Zohar81,

104

pyton

zlibbiblioteka obsługuje :

zlibModuł Pythona również je obsługuje.

wybierając windowBits

Ale zlibmoże rozpakować wszystkie te formaty:

  • do (dekompresji) deflateformatu, użyjwbits = -zlib.MAX_WBITS
  • do (dekompresji) zlibformatu, użyjwbits = zlib.MAX_WBITS
  • do (dekompresji) gzipformatu, użyjwbits = zlib.MAX_WBITS | 16

Zobacz dokumentację w http://www.zlib.net/manual.html#Advanced (sekcja inflateInit2)

przykłady

dane testowe:

>>> 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()
>>> 

oczywisty test na zlib:

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

test na 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'

test na 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'

dane są również kompatybilne z gzipmodułem:

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

automatyczne wykrywanie nagłówka (zlib lub gzip)

dodanie 32do windowBitsspowoduje wykrycie nagłówka

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

używając gzipzamiast tego

W przypadku gzipdanych z nagłówkiem gzip możesz użyć gzipmodułu bezpośrednio; ale proszę pamiętać, że pod maską , gzipzastosowań zlib.

fh = gzip.open('abc.gz', 'rb')
cdata = fh.read()
fh.close()

3
dlaczego tego kawałka złota nie ma w dokumentach w dokładnie takim formacie?
Ramon Moraes

prosimy o przesłanie żądania ściągnięcia / poprawki przeciwko cpythonowi, używając dowolnej z tych odpowiedzi.
dnozay

świetna odpowiedź na ciągi znaków, jakiś pomysł, jak to zrobić dla strumienia bez wczytywania całego pliku do pamięci?
Josh J

Dziękuję Ci. Mogę rozwiązać problem z dekompresją w moim kodzie źródłowym za pomocą Twojej odpowiedzi.
Bethlee

niewiarygodne, to jest samorodek złota… jednak nie mogę się powstrzymać od poczucia, że ​​są to „magiczne liczby”? gdzie w dokumentacji jest to wspomniane? spojrzałem, ale chyba naprawdę nie sprawdziłem wystarczająco mocno ... także notacja, której nie w pełni przestrzegam. Co oznacza | znaczy, czy to jest opcjonalne? i dlaczego deflate jest ujemne… MAX_WBITS jest stałą… 🙁
m1nkeh

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.