Piszę skrypt, który będzie działał z danymi pochodzącymi z oprzyrządowania jako strumieniami gzip. W około 90% przypadków moduł gzip
działa idealnie, ale niektóre strumienie powodują, że produkuje on IOError: Not a gzipped file
. Jeśli nagłówek programu gzip zostanie usunięty, a strumień deflacyjny zostanie przesłany bezpośrednio do zlib
, otrzymam zamiast niego Error -3 while decompressing data: incorrect header check
. Po około pół dnia uderzania głową o ścianę odkryłem, że strumienie, które mają problemy, zawierają pozornie losową liczbę dodatkowych bajtów (które nie są częścią danych gzip) dołączonych do końca.Jak mogę pracować z plikami Gzip, które zawierają dodatkowe dane?
Wydaje mi się dziwne, że Python nie może pracować z tymi plikami z dwóch powodów:
- Zarówno Gzip i 7zip są w stanie otworzyć te „wyściełane” pliki bez problemu. (. Gzip produkuje komunikat
decompression OK, trailing garbage ignored
, 7zip uda cicho) Zarówno docs Gzip i Python wydają się wskazywać, że to powinno działać: (Kopalnia nacisk)
Musi istnieć możliwość do wykryj koniec skompresowanych danych dowolną metodą kompresji, , niezależnie od faktycznego rozmiaru skompresowanych danych. W szczególności dekompresor musi być w stanie wykryć i pominąć niektóre dane dołączone do ważnego pliku skompresowanego w systemie plików rekord zorientowanych, lub gdy skompresowane dane można odczytać tylko z urządzenia w wielokrotnościami określony rozmiar bloku.
Wywołanie
close()
metoda AGzipFile
obiektu nie zamyka fileobj, ponieważ może chcesz dołączyć więcej materiału po danych skompresowanych. Umożliwia to również przekazanie obiektuStringIO
otwartego do zapisania jako fileobj i pobranie wynikowego bufora pamięci za pomocą metody obiektu obiektu.Python's
zlib.Decompress.unused_data
:Ciąg, który zawiera żadnych bajtów przeszłości końcu skompresowanych danych. Oznacza to, że pozostaje on
""
, dopóki nie będzie dostępny ostatni bajt zawierający dane kompresji. Jeśli cały ciąg znaków zawiera skompresowane dane, jest to pusty ciąg znaków o numerze""
.Jedynym sposobem określenia, gdzie kończy się ciąg skompresowanych danych, jest ich dekompresja. Oznacza to, że gdy skompresowane dane zawierają część większego pliku, jego koniec można znaleźć tylko poprzez odczytanie danych i podanie go przez jakiś niepustny ciąg w metodzie obiektu dekompresyjnego o numerze
decompress()
, dopóki atrybutunused_data
przestanie być atrybutem . pusty ciąg.
Oto cztery sposoby próbowałem. (Te przykłady są Python 3.1, ale Przetestowałem 2,5 i 2,7 i miał ten sam problem.)
# approach 1 - gzip.open
with gzip.open(filename) as datafile:
data = datafile.read()
# approach 2 - gzip.GzipFile
with open(filename, "rb") as gzipfile:
with gzip.GzipFile(fileobj=gzipfile) as datafile:
data = datafile.read()
# approach 3 - zlib.decompress
with open(filename, "rb") as gzipfile:
data = zlib.decompress(gzipfile.read()[10:])
# approach 4 - zlib.decompressobj
with open(filename, "rb") as gzipfile:
decompressor = zlib.decompressobj()
data = decompressor.decompress(gzipfile.read()[10:])
Czy robię coś źle?
UPDATE
porządku, podczas gdy problem z gzip
wydaje się być błąd w module, moi zlib
problemy są samookaleczenia. ;-)
Podczas kopania do gzip.py
zdałem sobie sprawę, co robiłem źle - domyślnie, zlib.decompress
i in. spodziewaj się strumieni zlib-owskich, a nie nagich strumieni deflacyjnych. Przekazując ujemną wartość dla wbits
, można powiedzieć zlib
, aby pominąć nagłówek zlib i odszyfrować surowy strumień. Obie te czynności:
# approach 5 - zlib.decompress with negative wbits
with open(filename, "rb") as gzipfile:
data = zlib.decompress(gzipfile.read()[10:], -zlib.MAX_WBITS)
# approach 6 - zlib.decompressobj with negative wbits
with open(filename, "rb") as gzipfile:
decompressor = zlib.decompressobj(-zlib.MAX_WBITS)
data = decompressor.decompress(gzipfile.read()[10:])
bym faktycznie spasowanych o trochę z 'wewnętrznych gzip' użytkownika podczas debugowania tego problemu, ale nie przyszło mi do głowy, aby naprawić tam problem i spakuj zmodyfikowany moduł za pomocą mojego skryptu.To brzydkie jak diabli, ale wygląda na to, że nadal może być najlepszą dostępną opcją. : -/ –
@Ben: Jest wystarczająco samodzielny, że przynajmniej nie jest to poważny koszt; tylko jeden plik. Jest to bardziej denerwujące w przypadku rodzimych modułów. –
Podczas pracy, zakładając, że nie łamie ona ograniczeń rozmiaru ani czasu na kodzie, można czytać bajt po chwili po otrzymaniu błędu. Dołącz każdy bajt na liście, gdy otrzymasz kolejny błąd IOError z parametrem "Nie jest to plik gzipowany", osiągnąłeś koniec danych, '' .znajdź i zwróć –