Разархивация структуры каталогов с Python

@Eran уже объяснил , как лучше решить эту проблему. Я объясню, почему происходит ConcurrentModificationException.

ConcurrentModificationException происходит потому, что вы изменяете источник потока. Вероятно, ваш Map будет HashMap или TreeMap или другой неконкурентной картой. Предположим, что это HashMap. Каждый поток поддерживается Spliterator . Если разделитель не имеет характеристик IMMUTABLE и CONCURRENT, тогда, как указано в документации:

После привязки Spliterator следует, в наилучшей степени, бросить ConcurrentModificationException, если структурные помехи обнаружено. Разделители, которые это делают, называются fail-fast .

blockquote>

Таким образом, HashMap.keySet().spliterator() не IMMUTABLE (поскольку этот Set можно изменить), а не CONCURRENT (одновременные обновления небезопасны для HashMap). Таким образом, он просто обнаруживает одновременные изменения и выбрасывает ConcurrentModificationException, как предписывает документация разделителя.

Также стоит ссылаться на документацию HashMap :

Итераторы, возвращенные всеми методами «сбора данных» этого класса, являются fail-fast : если карта структурно модифицирована в любое время после создания итератора, любым способом, кроме как через собственный итератор удалить метод, итератор будет бросать ConcurrentModificationException. Таким образом, перед лицом одновременной модификации итератор быстро и чисто, а не рискует произвольным, недетерминированным поведением в неопределенное время в будущем.

Обратите внимание, что неудачное поведение итератора не может быть гарантировано, поскольку, вообще говоря, невозможно сделать какие-либо серьезные гарантии при наличии несинхронизированной параллельной модификации. Неудачные итераторы бросают ConcurrentModificationException с наилучшей точки зрения. Поэтому было бы неправильно писать программу, зависящую от этого исключения за ее правильность: отказоустойчивое поведение итераторов должно использоваться только для обнаружения ошибок .

blockquote>

Пока он говорит только об итераторах, я считаю, что это одинаково для разделителей.

29
задан SilentGhost 12 March 2009 в 19:11
поделиться

8 ответов

Извлечение и extractall методы являются большими, если Вы находитесь на Python 2.6. Я должен использовать Python 2.5 на данный момент, таким образом, я просто должен создать каталоги, если они не существуют. Можно получить список каталогов с namelist() метод. Каталоги будут всегда заканчиваться наклонной чертой вправо (даже в Windows), например,

import os, zipfile

z = zipfile.ZipFile('myfile.zip')
for f in z.namelist():
    if f.endswith('/'):
        os.makedirs(f)

Вы, вероятно, не хотите делать это точно как этот (т.е. Вы, вероятно, хотели бы извлечь содержание zip-файла, поскольку Вы выполняете итерации по namelist), но Вы получаете идею.

23
ответ дан Jeff 28 November 2019 в 01:17
поделиться

Я знаю, что может быть немного поздно сказать это, но Джефф прав. Это так же просто, как:

import os
from zipfile import ZipFile as zip

def extractAll(zipName):
    z = zip(zipName)
    for f in z.namelist():
        if f.endswith('/'):
            os.makedirs(f)
        else:
            z.extract(f)

if __name__ == '__main__':
    zipList = ['one.zip', 'two.zip', 'three.zip']
    for zip in zipList:
        extractAll(zipName)
6
ответ дан Balaji Kandasamy 14 October 2019 в 07:44
поделиться

Это кажется, что Вы пытаетесь работать, разархивировали для извлечения zip.

было бы лучше использовать модуль python zipfile и поэтому сделать извлечение в Python.

import zipfile

def extract(zipfilepath, extractiondir):
    zip = zipfile.ZipFile(zipfilepath)
    zip.extractall(path=extractiondir)
2
ответ дан Douglas Leeder 14 October 2019 в 07:44
поделиться

Если, как и я, вы должны извлечь полный zip-архив со старым выпуском Python (в моем случае 2.4), вот что я придумал (основываясь на ответе Джеффа):

import zipfile
import os

def unzip(source_file_path, destination_dir):
    destination_dir += '/'
    z = zipfile.ZipFile(source_file_path, 'r')
    for file in z.namelist():
        outfile_path = destination_dir + file
        if file.endswith('/'):
            os.makedirs(outfile_path)
        else:
            outfile = open(outfile_path, 'wb')
            outfile.write(z.read(file))
            outfile.close()
    z.close()
2
ответ дан Apteryx 14 October 2019 в 07:44
поделиться

Существует очень простой способ при использовании Python 2.6: метод extractall .

Однако начиная с zipfile модуль реализован полностью в Python без любых расширений C, можно, вероятно, скопировать его из 2,6 установок и использовать его с более старой версией Python; можно найти это легче, чем необходимость повторно реализовать функциональность сами. Однако сама функция довольно коротка:

def extractall(self, path=None, members=None, pwd=None):
    """Extract all members from the archive to the current working
       directory. `path' specifies a different directory to extract to.
       `members' is optional and must be a subset of the list returned
       by namelist().
    """
    if members is None:
        members = self.namelist()

    for zipinfo in members:
        self.extract(zipinfo, path, pwd)
3
ответ дан Eli Courtwright 14 October 2019 в 07:44
поделиться

Не не доверяйте extract () или extractall ().

Эти методы слепо извлекают файлы по путям, указанным в их именах файлов. Но имена файлов ZIP могут быть любыми, включая опасные строки, такие как «x /../../../ etc / passwd». Извлеките такие файлы, и вы могли бы просто поставить под угрозу весь ваш сервер.

Может быть, это следует считать сообщаемой дырой в безопасности в модуле zipfile в Python, но любое количество архиваторов в прошлом демонстрировало точно такое же поведение. Чтобы безопасно разархивировать ZIP-файл со структурой папок, вам необходима тщательная проверка каждого пути к файлу.

16
ответ дан bobince 14 October 2019 в 07:44
поделиться

Я испытал это и могу воспроизвести его. extractall метод, как предложено другими ответами, не решает проблему. Это походит на ошибку в zipfile модуле мне (возможно, только для Windows?), если я не неправильно понимаю, как структурированы zipfiles.

testa\
testa\testb\
testa\testb\test.log
> test.zip

>>> from zipfile import ZipFile
>>> zipTest = ZipFile("C:\\...\\test.zip")
>>> zipTest.extractall("C:\\...\\")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "...\zipfile.py", line 940, in extractall
  File "...\zipfile.py", line 928, in extract
  File "...\zipfile.py", line 965, in _extract_member
IOError: [Errno 2] No such file or directory: 'C:\\...\\testa\\testb\\test.log'

Если я делаю a printdir(), Я получаю это (первый столбец):

>>> zipTest.printdir()
File Name
testa/testb/
testa/testb/test.log

Если я пытаюсь извлечь просто первую запись, как это:

>>> zipTest.extract("testa/testb/")
'C:\\...\\testa\\testb'

На диске это приводит к созданию папки testa, с файлом testb внутри. Это - по-видимому, причина почему последующая попытка извлечь test.log сбои; testa\testb файл, не папка.

Редактирование № 1: Если Вы извлекаете просто файл, то он работает:

>>> zipTest.extract("testa/testb/test.log")
'C:\\...\\testa\\testb\\test.log'

Редактирование № 2: код Jeff является способом пойти; выполните итерации через namelist; если это - каталог, создайте каталог. Иначе извлеките файл.

8
ответ дан DNS 28 November 2019 в 01:17
поделиться

Обратите внимание, что zip-файлы могут содержать записи для каталогов, а также для файлов. При создании архивов с помощью команды zip передайте параметр -D , чтобы запретить явное добавление записей каталога в архив. Когда метод Python 2.6 ZipFile.extractall проходит через запись каталога, кажется, что вместо него создается файл . Поскольку записи в архиве не обязательно в порядке, это приводит к частому сбою ZipFile.extractall , поскольку он пытается создать файл в подкаталоге файла. Если у вас есть архив, который вы хотите использовать с модулем Python, просто извлеките его и повторно заархивируйте с параметром -D . Вот небольшой фрагмент, который я использовал некоторое время, чтобы сделать именно это:

P=`pwd` && 
Z=`mktemp -d -t zip` && 
pushd $Z && 
unzip $P/<busted>.zip && 
zip -r -D $P/<new>.zip . && 
popd && 
rm -rf $Z

Замените .zip и .zip настоящими именами файлов относительно Текущий каталог. Затем просто скопируйте все это и вставьте в командную оболочку, и он создаст новый архив, готовый к работе с Python 2.6. Там есть команда zip , которая удаляет эти записи каталога без разархивирования, но IIRC она вела себя странно в различных средах оболочки или конфигурациях zip.

1
ответ дан 28 November 2019 в 01:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: