Как симулировать ZipFile.open в Python 2.5? - PullRequest
4 голосов
/ 23 сентября 2010

Я хочу извлечь файл из zip-файла по указанному пути, игнорируя путь к файлу в архиве.Это очень легко в Python 2.6 (моя строка документации длиннее, чем код)

import shutil
import zipfile

def extract_from_zip(name, dest_path, zip_file):
    """Similar to zipfile.ZipFile.extract but extracts the file given by name
    from the zip_file (instance of zipfile.ZipFile) to the given dest_path
    *ignoring* the filename path given in the archive completely
    instead of preserving it as extract does.
    """
    dest_file = open(dest_path, 'wb')
    archived_file = zip_file.open(name)
    shutil.copyfileobj(archived_file, dest_file)


 extract_from_zip('path/to/file.dat', 'output.txt', zipfile.ZipFile('test.zip', 'r'))

Но в Python 2.5 метод ZipFile.open недоступен.Я не смог найти решение для stackoverflow, но в этом сообщении на форуме было хорошее решение, которое использует ZipInfo.file_offset для поиска нужной точки в zip и использования zlib.decompressobj чтобы распаковать байты оттуда.К сожалению, ZipInfo.file_offset был удален в Python 2.5!

Итак, учитывая, что все, что у нас есть в Python 2.5 - это ZipInfo.header_offset, я решил, что мне просто нужно проанализировать и пропуститьструктура заголовка, чтобы получить к смещению файла самостоятельно.Используя Википедию в качестве ссылки (я знаю), я придумал это гораздо более длинное и не очень элегантное решение.

import zipfile
import zlib

def extract_from_zip(name, dest_path, zip_file):
    """Python 2.5 version :("""
    dest_file = open(dest_path, 'wb')
    info = zip_file.getinfo(name)
    if info.compress_type == zipfile.ZIP_STORED:
        decoder = None
    elif info.compress_type == zipfile.ZIP_DEFLATED:
        decoder = zlib.decompressobj(-zlib.MAX_WBITS)
    else:
        raise zipfile.BadZipFile("Unrecognized compression method")

    # Seek over the fixed size fields to the "file name length" field in
    # the file header (26 bytes). Unpack this and the "extra field length"
    # field ourselves as info.extra doesn't seem to be the correct length.
    zip_file.fp.seek(info.header_offset + 26)
    file_name_len, extra_len = struct.unpack("<HH", zip_file.fp.read(4))
    zip_file.fp.seek(info.header_offset + 30 + file_name_len + extra_len)

    bytes_to_read = info.compress_size

    while True:
        buff = zip_file.fp.read(min(bytes_to_read, 102400))
        if not buff:
            break
        bytes_to_read -= len(buff)
        if decoder:
            buff = decoder.decompress(buff)
        dest_file.write(buff)

    if decoder:
        dest_file.write(decoder.decompress('Z'))
        dest_file.write(decoder.flush())

Обратите внимание, как я распаковываю и читаю поле, которое дает длинудополнительное поле, потому что вызов len для атрибута ZipInfo.extra дает на 4 байта меньше, что приводит к неправильному вычислению смещения.Возможно, я что-то здесь упускаю?

Может кто-нибудь улучшить это решение для Python 2.5?

Редактировать: Я должен был сказать, очевидное решение, предложенное ChrisAdams

dest_file.write(zip_file.read(name))

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

Кроме того, обновление Python является очевидным решением, но оно совершенно не в моих руках и практически невозможно.

Ответы [ 3 ]

4 голосов
/ 23 сентября 2010

Не проверял этот бит, но я использую что-то очень похожее в Python 2.4

import zipfile

def extract_from_zip(name, dest_path, zip_file):
    dest_file = open(dest_path, 'wb')
    dest_file.write(zip_file.read(name))
    dest_file.close()

extract_from_zip('path/to/file/in/archive.dat', 
        'output.txt', 
        zipfile.ZipFile('test.zip', 'r'))
1 голос
/ 02 сентября 2011

Я знаю, что немного опоздал на вечеринку по этому вопросу, но у меня точно такая же проблема.

Решение, которое я использовал, состояло в том, чтобы скопировать версию zipfile на python 2.6.6, поместить в папку (я назвал ее python_fix) и импортировать ее вместо:

python_fix/zipfile.py

Тогда в коде:

import python_fix.zipfile as zipfile

Оттуда я смог использовать версию zipfile 2.6.6 с интерпретатором python 2.5.1 (версии 2.7.X терпят неудачу на «with» с этой версией »)

Надеюсь, это поможет кому-то еще, используя древние технологии.

0 голосов
/ 27 октября 2010

Учитывая мои ограничения, похоже, что был дан ответ на мой вопрос: проанализируйте структуру ZipFile самостоятельно и используйте zlib.decompressobj, чтобы разархивировать байты, как только вы их нашли.

Если у вас нет (/ страдают от) моих ограничений, вы можете найти лучшие ответы здесь:

  1. Если вы можете, просто обновите Python 2.5 до 2.6 (или позже!), Как предложено в комментарии Daenyth.
  2. Если в zip-архиве есть только небольшие файлы, которые могут быть загружены в память на 100%, используйте ответ ChrisAdams
  3. Если вы можете ввести зависимость от внешней утилиты, сделайте соответствующий системный вызов для /usr/bin/unzip или аналогичный, как предложено в Ответ Влада
...