Python Zip-файл не распаковывает папки для Windows Zip-архив

1

У меня есть zip файл, который был создан на Windows машине с помощью этого инструмента System.IO.Compression.ZipFile (этот zip-архив содержит много файлов и папок). У меня есть код python, который работает на машине Linux (точнее, малина pi), которая должна распаковать архив и создать все необходимые папки и файлы. Я использую Python 3.5.0 и библиотеку zipfile, это пример кода:

import zipfile

zip = zipfile.ZipFile("MyArchive.zip","r")
zip.extractall()
zip.close()

Теперь, когда я запускаю этот код, а не получаю красивое распакованное дерево каталогов, я получаю все файлы в корневом каталоге с такими странными именами, как Folder1\Folder2\MyFile.txt.

Мое предположение заключается в том, что, поскольку zip-архив был создан в Windows, а разделитель каталога в Windows - \ тогда как в Linux это /, библиотека zipfile python обрабатывает \ как часть имени файла, а не разделителя каталогов. Также обратите внимание, что когда я извлекаю этот архив вручную (не через код python), вся папка создается, как ожидалось, поэтому кажется, что это определенно проблема библиотеки zipfile. Другое примечание: для zip-архивов, созданных с помощью другого инструмента (а не System.IO.Compression.ZipFile), он работает нормально, используя тот же код python.

Любое понимание того, что происходит и как это исправить?

  • 0
    Zip-файлы хранят все в корне. Иерархия фактически создается через косые черты в именах файлов.
  • 0
    Покажите, как вы извлекаете архив вручную
Показать ещё 6 комментариев
Теги:
python-3.x
zipfile
archive

2 ответа

2
Лучший ответ

path.sep что, хотя Windows распознает как \ (path.sep), так и / (path.altsep) в качестве разделителей путей, Linux распознает / (path.sep).

Как показывает ответ @blhsing, существующая реализация ZipFile всегда гарантирует, что path.sep и / считаются допустимыми разделительными символами. Это означает, что в Linux \ рассматривается как буквальная часть имени файла. Чтобы изменить это, вы можете установить os.altsep в \, поскольку он проверяется, если он не является ни os.altsep из пустых.

Если вы идете по пути изменения самого ZipFile, как и другой ответ, просто добавьте строку для слепого изменения \ to path.sep, так как / всегда изменяется в любом случае. Таким образом, /, \ и, возможно, path.altsep будут преобразованы в path.sep. Это то, что делает инструмент командной строки.

1

Это действительно ошибка модуля zipfile, где в ZipFile._extract_member() есть следующая строка, чтобы вслепую заменить '/' в именах файлов специальным разделителем путей по умолчанию, когда он также должен искать '\\':

arcname = member.filename.replace('/', os.path.sep)

Вы можете исправить это, переопределив ZipFile._extract_member() с версией, которая напрямую копируется из исходного кода, но с приведенной выше строкой:

from zipfile import ZipFile, ZipInfo
import shutil
import os
def _extract_member(self, member, targetpath, pwd):
    """Extract the ZipInfo object 'member' to a physical
       file on the path targetpath.
    """
    if not isinstance(member, ZipInfo):
        member = self.getinfo(member)

    if os.path.sep == '/':
        arcname = member.filename.replace('\\', os.path.sep)
    else:
        arcname = member.filename.replace('/', os.path.sep)

    if os.path.altsep:
        arcname = arcname.replace(os.path.altsep, os.path.sep)
    # interpret absolute pathname as relative, remove drive letter or
    # UNC path, redundant separators, "." and ".." components.
    arcname = os.path.splitdrive(arcname)[1]
    invalid_path_parts = ('', os.path.curdir, os.path.pardir)
    arcname = os.path.sep.join(x for x in arcname.split(os.path.sep)
                               if x not in invalid_path_parts)
    if os.path.sep == '\\':
        # filter illegal characters on Windows
        arcname = self._sanitize_windows_name(arcname, os.path.sep)

    targetpath = os.path.join(targetpath, arcname)
    targetpath = os.path.normpath(targetpath)

    # Create all upper directories if necessary.
    upperdirs = os.path.dirname(targetpath)
    if upperdirs and not os.path.exists(upperdirs):
        os.makedirs(upperdirs)

    if member.is_dir():
        if not os.path.isdir(targetpath):
            os.mkdir(targetpath)
        return targetpath

    with self.open(member, pwd=pwd) as source, \
            open(targetpath, "wb") as target:
        shutil.copyfileobj(source, target)

    return targetpath
ZipFile._extract_member = _extract_member
  • 0
    Это не имеет особого смысла, учитывая, что метод уже делает это.
  • 0
    Исправлено с условным оператором вместо.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню