Sto scrivendo uno script che funzionerà con i dati provenienti dalla strumentazione come flussi gzip. In circa il 90% dei casi, il modulo gzip
funziona perfettamente, ma alcuni flussi causano la produzione di IOError: Not a gzipped file
. Se l'intestazione gzip viene rimossa e il flusso di deflate alimentato direttamente a zlib
, ho invece ottenuto Error -3 while decompressing data: incorrect header check
. Dopo circa mezza giornata di sbattere la testa contro il muro, ho scoperto che i flussi che hanno problemi contengono un numero apparentemente casuale di byte extra (che non fanno parte dei dati gzip) aggiunti alla fine.Come posso lavorare con file Gzip che contengono dati aggiuntivi?
Mi sembra strano che Python non può funzionare con questi file per due ragioni:
- Sia Gzip e 7zip sono in grado di aprire questi file "imbottiti" senza alcun problema. (. Gzip genera il messaggio
decompression OK, trailing garbage ignored
, 7zip riesce in silenzio) Entrambi i documenti Gzip e Python sembrano indicare che questo dovrebbe funzionare: (sottolineatura mia)
deve essere possibile a rilevare la fine dei dati compressi con qualsiasi metodo di compressione, indipendentemente dalla dimensione effettiva dei dati compressi. In particolare, decompressore deve essere in grado di rilevare e saltare dati aggiuntivi annesse ad un file compresso valido su un file system record-oriented, o quando i dati compressi possono essere letti solo da un dispositivo in multipli di un alcune dimensioni del blocco.
Chiamare il metodo di un oggetto
GzipFile
close()
non chiude fileobj, dal momento che si potrebbe desiderare di aggiungere più materiale dopo i dati compressi. Ciò consente inoltre di passare un oggettoStringIO
aperto per la scrittura come fileobj e recuperare il buffer di memoria risultante utilizzando il metodogetvalue()
dell'oggettoStringIO
.Python's
zlib.Decompress.unused_data
:Una stringa che contiene ogni byte successivo alla fine dei dati compressi. Cioè, questo rimane
""
finché non è disponibile l'ultimo byte che contiene dati di compressione. Se l'intera stringa risulta contenere dati compressi, questa è""
, la stringa vuota.L'unico modo per determinare dove finisce una stringa di dati compressi è in realtà decomprimerlo. Ciò significa che quando i dati compressi sono contenuti in un file più grande, è possibile trovarne solo la fine tramite lettura dei dati e alimentazione seguita da una stringa non vuota nel metodo dell'oggetto decompressione fino a quando l'attributo
unused_data
non è più la stringa vuota.
Qui ci sono quattro approcci che ho provato. (Questi esempi sono Python 3.1, ma ho provato 2.5 e 2.7 e ha avuto lo stesso problema.)
# 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:])
Sto facendo qualcosa di sbagliato?
UPDATE
Va bene, mentre il problema con gzip
sembra essere un bug nel modulo, i miei zlib
problemi sono auto-inflitta. ;-)
Mentre scavo in gzip.py
mi sono reso conto di cosa stavo facendo male: per impostazione predefinita, zlib.decompress
e altri. si aspettano flussi con avvolgimento di zlib, non flussi di deflazione nudi. Inserendo un valore negativo per wbits
, è possibile indicare a zlib
di ignorare l'intestazione di zlib e decrittografare il flusso non elaborato. Entrambi funzionano:
# 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:])
mi piacerebbe davvero muck circa un po 'con 'interni di gzip' durante il debug del problema, ma non era venuto in mente di risolvere il problema c'è e pacchetto il modulo modificato con il mio script.È brutto da morire, ma sembra che potrebbe essere ancora la migliore opzione disponibile. : -/ –
@ Ben: È abbastanza autonomo che non è un costo importante, almeno; solo un file. È più fastidioso con i moduli nativi. –
Come soluzione, supponendo che non interrompa le dimensioni o i limiti di tempo del codice, è possibile leggere un byte alla volta dopo aver ricevuto l'errore. Aggiungi ogni byte in un elenco, quando ricevi un altro errore IOError con un parametro "Non un file gzip", hai raggiunto la fine dei dati, "" .invia e restituisci –