Распаковка части файла .gz с использованием python - PullRequest
8 голосов
/ 14 ноября 2009

Так вот в чем проблема. У меня есть файл sample.gz размером примерно 60 КБ. Я хочу распаковать первые 2000 байтов этого файла. Я сталкиваюсь с ошибкой проверки CRC, я думаю, потому что поле CRC gzip появляется в конце файла, и для его распаковки требуется весь файл gzip. Есть ли способ обойти это? Меня не волнует проверка CRC. Даже если мне не удастся распаковать из-за плохого CRC, это нормально. Есть ли способ обойти это и разархивировать частичные файлы .gz?

Код, который у меня пока есть,

import gzip
import time
import StringIO

file = open('sample.gz', 'rb')
mybuf = MyBuffer(file)
mybuf = StringIO.StringIO(file.read(2000))
f = gzip.GzipFile(fileobj=mybuf)
data = f.read()
print data

Произошла ошибка

File "gunzip.py", line 27, in ?
    data = f.read()
File "/usr/local/lib/python2.4/gzip.py", line 218, in read
  self._read(readsize)
File "/usr/local/lib/python2.4/gzip.py", line 273, in _read
  self._read_eof()
File "/usr/local/lib/python2.4/gzip.py", line 309, in _read_eof
  raise IOError, "CRC check failed"
IOError: CRC check failed

Также есть ли способ использовать модуль zlib для этого и игнорировать заголовки gzip?

Ответы [ 4 ]

13 голосов
/ 04 сентября 2013

Проблема с модулем gzip заключается не в том, что он не может распаковать частичный файл, ошибка возникает только в конце, когда он пытается проверить контрольную сумму распакованного содержимого. (Исходная контрольная сумма сохраняется в конце сжатого файла, поэтому проверка никогда не будет работать с частичным файлом.)

Ключ в том, чтобы обмануть gzip и пропустить проверку. Ответ от caesar0301 делает это путем изменения исходного кода gzip, но не нужно заходить так далеко, подойдет простое исправление обезьяны. Я написал этот менеджер контекста, чтобы временно заменить gzip.GzipFile._read_eof, пока я распаковываю частичный файл:

import contextlib

@contextlib.contextmanager
def patch_gzip_for_partial():
    """
    Context manager that replaces gzip.GzipFile._read_eof with a no-op.

    This is useful when decompressing partial files, something that won't
    work if GzipFile does it's checksum comparison.

    """
    _read_eof = gzip.GzipFile._read_eof
    gzip.GzipFile._read_eof = lambda *args, **kwargs: None
    yield
    gzip.GzipFile._read_eof = _read_eof

Пример использования:

from cStringIO import StringIO

with patch_gzip_for_partial():
    decompressed = gzip.GzipFile(StringIO(compressed)).read()
12 голосов
/ 14 ноября 2009

Мне кажется, что вам нужно посмотреть Python zlib вместо библиотеки

Формат GZIP опирается на zlib, но вводит концепцию сжатия на уровне файлов вместе с проверкой CRC, и это кажется тем, что вам сейчас не нужно / не нужно.

См., Например, эти фрагменты кода от Dough Hellman

Редактировать : код на сайте Доуба Хеллмана показывает только, как сжимать или распаковывать с помощью zlib. Как указано выше, GZIP - это "zlib с конвертом", и вам нужно будет декодировать envellope перед тем, как перейти к сжатым zlib данным per se . Вот дополнительная информация, это действительно не так сложно:

  • Подробнее о формате GZIP
  • Этот формат начинается с 10-байтового заголовка, за которым следуют необязательные несжатые элементы, такие как имя файла или комментарий, за которыми следуют сжатые zlib данные, за которыми следует CRC-32 (точнее CRC Adler32). ).
  • Используя модуль Python для структуры , анализ заголовка должен быть относительно простым
  • Последовательность zlib (или ее первые несколько тысяч байтов, поскольку это то, что вы хотите сделать) затем может быть распакована с помощью модуля zlib в python, как показано в примерах выше
  • Возможные проблемы, которые необходимо решить: если в архиве GZip имеется более одного файла и если второй файл начинается в пределах блока из нескольких тысяч байтов, мы хотим распаковать его.

Извините, что не предоставил ни простой процедуры, ни готового фрагмента, однако декодирование файла с указанием выше должно быть относительно быстрым и простым.

10 голосов
/ 14 ноября 2009

Я не вижу никакой возможной причины, по которой вы захотите распаковать первые 2000 сжатых байтов. В зависимости от данных это может распаковать любое количество выходных байтов.

Конечно, вы хотите распаковать файл и остановиться, когда распаковали столько файлов, сколько вам нужно, например:

f = gzip.GzipFile(fileobj=open('postcode-code.tar.gz', 'rb'))
data = f.read(4000)
print data

AFAIK, это не приведет к чтению всего файла. Он будет читать только столько, сколько необходимо для получения первых 4000 байтов.

2 голосов
/ 12 мая 2013

Я также сталкиваюсь с этой проблемой, когда использую свой скрипт на python для чтения сжатых файлов, созданных утилитой gzip под Linux, и оригинальные файлы были потеряны.

Прочитав реализацию gzip.py Python, я обнаружил, что gzip.GzipFile имеет похожие методы класса File и использует zip-модуль python для обработки сжатия / сжатия данных. В то же время для проверки CRC каждого файла также присутствует метод _read_eof ().

Но в некоторых ситуациях, например при обработке потока или файла .gz без правильного CRC (моя проблема), IreadError («Проверка CRC не удалась») будет вызвана _read_eof (). Поэтому я пытаюсь изменить модуль gzip, чтобы отключить проверку CRC, и, наконец, эта проблема исчезла.

def _read_eof(self):
    pass

https://github.com/caesar0301/PcapEx/blob/master/live-scripts/gzip_mod.py

Я знаю, что это грубое решение, но это экономит много времени, чтобы переписать некоторые низкоуровневые методы с использованием zip-модуля, например, считывание данных chuck by chuck из сжатых файлов и извлечение данных построчно, большинство который присутствовал в модуле gzip.

Jamin

...