У меня есть 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.
Любое понимание того, что происходит и как это исправить?
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
. Это то, что делает инструмент командной строки.
Это действительно ошибка модуля 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