Я пишу скрипт, который будет работать с данными, поступающими из инструментальных средств, в виде потоков gzip.Примерно в 90% случаев модуль gzip
работает отлично, но некоторые потоки вызывают его выдачу IOError: Not a gzipped file
.Если заголовок gzip удален и поток deflate подается прямо на zlib
, я получаю Error -3 while decompressing data: incorrect header check
.Примерно через полдня ударившись головой о стену, я обнаружил, что потоки, в которых возникают проблемы, содержат на первый взгляд случайное количество дополнительных байтов (которые не являются частью данных gzip), добавленных в конец.
Мне кажется странным, что Python не может работать с этими файлами по двум причинам:
- И Gzip, и 7zip могут открывать эти "дополненные" файлы без проблем.(Gzip выдает сообщение
decompression OK, trailing garbage ignored
, 7zip выполняет молча.) Кажется, что и документы Gzip, и Python указывают, что это должно работать: (выделено мое)
Gzip's format.txt :
Должна быть предусмотрена возможность обнаружения конца сжатых данных любым методом сжатия, независимо от фактического размера сжатых данных. В частности, декомпрессор должен иметь возможность обнаруживать и пропускать дополнительные данные, добавленные в действительный сжатый файл в файловой системе с ориентированной на запись, или когда сжатые данные могут быть считаны с устройства только кратнымиопределенный размер блока.
gzip.GzipFile` Python :
Вызов GzipFile
объекта close()
метода объекта не закрывается fileobj , , так как вы можете добавить больше материала после сжатых данных .Это также позволяет передавать объект StringIO
, открытый для записи, как fileobj и извлекать полученный буфер памяти, используя метод getvalue()
объекта StringIO
.
Python's 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:])
Я что-то не так делаю?
ОБНОВЛЕНИЕ
Хорошо, хотя проблема с gzip
, похоже, является ошибкой в модуле, мои zlib
проблемы возникают сами собой.; -)
При копании в gzip.py
я понял, что делаю неправильно - по умолчанию zlib.decompress
и др.ожидайте zlib-обернутые потоки, а не голые потоки с дефляцией.Передав отрицательное значение для 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:])