Estoy escribiendo un script que funcionará con datos provenientes de la instrumentación como flujos gzip. En aproximadamente el 90% de los casos, el módulo gzip
funciona perfectamente, pero algunas de las secuencias hacen que produzca IOError: Not a gzipped file
. Si el encabezado gzip se elimina y la secuencia desinflada se alimenta directamente a zlib
, en su lugar obtengo Error -3 while decompressing data: incorrect header check
. Después de medio día de golpear mi cabeza contra la pared, descubrí que las secuencias que están teniendo problemas contienen una cantidad aparentemente aleatoria de bytes adicionales (que no son parte de los datos de gzip) añadidos al final.¿Cómo puedo trabajar con archivos Gzip que contienen datos adicionales?
Me parece extraño que Python no puede trabajar con estos archivos por dos razones:
- Tanto gzip y 7zip son capaces de abrir estos archivos "acolchados" sin problema. (. Gzip produce el mensaje
decompression OK, trailing garbage ignored
, 7zip tiene éxito en silencio) Tanto los documentos gzip y Python parecen indicar que esto debería funcionar: (énfasis mío)
debe ser posible a detectar el final de los datos comprimidos con cualquier método de compresión, independientemente del tamaño real de los datos comprimidos. En particular, el descompresor debe ser capaz de detectar y omitir datos adicionales adjuntas a un archivo comprimido válida en un sistema de archivos orientados a registros, o cuando los datos comprimidos sólo se pueden leer desde un dispositivo en múltiplos de un cierto tamaño de bloque
Llamando al método de un objeto
GzipFile
close()
no se cierra fileobj, ya que es posible que desee añadir más material después de que los datos comprimidos. Esto también le permite pasar un objetoStringIO
abierto para escribir como fileobj, y recuperar el búfer de memoria resultante utilizando el métodoStringIO
del objetogetvalue()
.Python's
zlib.Decompress.unused_data
:una cadena que contiene cualquier bytes más allá del final de los datos comprimidos. Es decir, esto sigue siendo
""
hasta que esté disponible el último byte que contiene datos de compresión. Si toda la secuencia resultó contener datos comprimidos, esta es""
, la cadena vacía.La única manera de determinar dónde termina una cadena de datos comprimidos es descomprimiéndolo realmente. Esto significa que cuando los datos comprimidos contienen parte de un archivo más grande, solo puede encontrar el final al leyendo los datos y alimentándolos seguido por una cadena no vacía en el método
decompress()
de un objeto de descompresión hasta que el atributounused_data
ya no sea la cadena vacía
Éstos son los cuatro enfoques que he probado. (Estos son ejemplos de Python 3.1, pero he probado 2.5 y 2.7 y tenía el mismo 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:])
¿Estoy haciendo algo mal?
ACTUALIZACIÓN
bien, mientras que el problema con gzip
parece ser un error en el módulo, mis zlib
problemas son auto-infligidas. ;-)
Mientras investigaba en gzip.py
me di cuenta de lo que estaba haciendo mal - de forma predeterminada, zlib.decompress
y otros. espere secuencias envueltas en zlib, no corrientes de desinflado al descubierto. Al pasar un valor negativo para wbits
, puede indicar zlib
para omitir el encabezado zlib y descromover la secuencia sin formato. Ambos funcionan:
# 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:])
realidad me mucked acerca un poco con 'internos de gzip' al depurar el problema, pero no se me había ocurrido para arreglar el problema allí y el paquete del módulo modificado con mi script.Es feo como el infierno, pero parece que todavía puede ser la mejor opción disponible. : -/ –
@Ben: es lo suficientemente autónomo como para que no sea un costo importante, al menos; solo un archivo Es más molesto con los módulos nativos. –
Como solución temporal, suponiendo que no interrumpe las restricciones de tamaño o tiempo en su código, puede leer en un byte a la vez después de recibir el error. Añada cada byte en una lista, cuando reciba otro IOError con un parámetro 'No un archivo comprimido', ha llegado al final de los datos, ''. Únase a él y devuelva –