Я пишу script, который будет работать с данными, поступающими из инструментария в виде потоков gzip. Примерно в 90% случаев модуль gzip
работает отлично, но некоторые потоки заставляют его создавать IOError: Not a gzipped file
. Если заголовок gzip удаляется, а поток дефляции подается непосредственно на zlib
, вместо этого я получаю Error -3 while decompressing data: incorrect header check
. Примерно через пол дня, ударив головой о стену, я обнаружил, что потоки, которые имеют проблемы, содержат, по-видимому, случайное количество дополнительных байтов (которые не являются частью данных gzip), прилагаемых к концу.
Мне кажется странным, что Python не может работать с этими файлами по двум причинам:
- Оба Gzip и 7zip могут открывать эти "проложенные" файлы без проблем. (Gzip выдает сообщение
decompression OK, trailing garbage ignored
, 7zip успешно завершает работу.) -
Оба документа Gzip и Python, по-видимому, указывают, что это должно работать: (акцент мой)
Должно быть возможно обнаруживать конец сжатых данных любым способом сжатия, независимо от фактического размера сжатых данных. В частности, декомпрессор должен иметь возможность обнаруживать и пропускать дополнительные данные в действительный сжатый файл в файловой системе, ориентированной на запись, или когда сжатые данные могут считываться только с устройства в кратном виде определенный размер блока.
Вызов метода
GzipFile
objectsclose()
не закрывает fileobj, , поскольку вы можете добавить больше материала после сжатых данных. Это также позволяет передать объектStringIO
, открытый для записи как fileobj, и получить полученный буфер памяти с помощью методаStringIO
objectsgetvalue()
.Python
zlib.Decompress.unused_data
:Строка, содержащая любые байты, прошедшие после конца сжатых данных. То есть, это остается
""
, пока не будет доступен последний байт, содержащий данные сжатия. Если вся строка оказалась сжатой, это""
, пустая строка.Единственный способ определить, где заканчивается строка сжатых данных, - это фактически декомпрессировать ее. Это означает, что когда сжатые данные содержат часть более крупного файла, вы можете найти только его конец с помощью чтения данных и подачи его, за которым следует некоторая непустая строка, в объекты декомпрессии
decompress()
пока атрибутunused_data
больше не является пустой строкой.
Вот четыре подхода, которые я пробовал. (Эти примеры - Python 3.1, но я тестировал 2.5 и 2.7 и имел ту же проблему.)
# 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:])
Я что-то делаю неправильно?
UPDATE
Хорошо, в то время как проблема с gzip
кажется ошибкой в модуле, мои проблемы zlib
самонастраиваются.; -)
Во время копания в gzip.py
я понял, что я делаю неправильно - по умолчанию, zlib.decompress
et al. ожидают потоки zlib-wrapped, а не голые потоки спуска. Передавая отрицательное значение для wbits
, вы можете сказать zlib
пропустить заголовок zlib и дефрагментировать исходный поток. Обе эти работы:
# 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:])