@Eran уже объяснил , как лучше решить эту проблему. Я объясню, почему происходит ConcurrentModificationException
.
ConcurrentModificationException
происходит потому, что вы изменяете источник потока. Вероятно, ваш Map
будет HashMap
или TreeMap
или другой неконкурентной картой. Предположим, что это HashMap
. Каждый поток поддерживается Spliterator
. Если разделитель не имеет характеристик IMMUTABLE
и CONCURRENT
, тогда, как указано в документации:
После привязки Spliterator следует, в наилучшей степени, бросить
blockquote>ConcurrentModificationException
, если структурные помехи обнаружено. Разделители, которые это делают, называются fail-fast .Таким образом,
HashMap.keySet().spliterator()
неIMMUTABLE
(поскольку этотSet
можно изменить), а неCONCURRENT
(одновременные обновления небезопасны дляHashMap
). Таким образом, он просто обнаруживает одновременные изменения и выбрасываетConcurrentModificationException
, как предписывает документация разделителя.Также стоит ссылаться на документацию
HashMap
:Итераторы, возвращенные всеми методами «сбора данных» этого класса, являются fail-fast : если карта структурно модифицирована в любое время после создания итератора, любым способом, кроме как через собственный итератор удалить метод, итератор будет бросать
ConcurrentModificationException
. Таким образом, перед лицом одновременной модификации итератор быстро и чисто, а не рискует произвольным, недетерминированным поведением в неопределенное время в будущем.Обратите внимание, что неудачное поведение итератора не может быть гарантировано, поскольку, вообще говоря, невозможно сделать какие-либо серьезные гарантии при наличии несинхронизированной параллельной модификации. Неудачные итераторы бросают
blockquote>ConcurrentModificationException
с наилучшей точки зрения. Поэтому было бы неправильно писать программу, зависящую от этого исключения за ее правильность: отказоустойчивое поведение итераторов должно использоваться только для обнаружения ошибок .Пока он говорит только об итераторах, я считаю, что это одинаково для разделителей.
Извлечение и 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), но Вы получаете идею.
Я знаю, что может быть немного поздно сказать это, но Джефф прав. Это так же просто, как:
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)
Это кажется, что Вы пытаетесь работать, разархивировали для извлечения zip.
было бы лучше использовать модуль python zipfile
и поэтому сделать извлечение в Python.
import zipfile
def extract(zipfilepath, extractiondir):
zip = zipfile.ZipFile(zipfilepath)
zip.extractall(path=extractiondir)
Если, как и я, вы должны извлечь полный 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()
Существует очень простой способ при использовании 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)
Не не доверяйте extract () или extractall ().
Эти методы слепо извлекают файлы по путям, указанным в их именах файлов. Но имена файлов ZIP могут быть любыми, включая опасные строки, такие как «x /../../../ etc / passwd». Извлеките такие файлы, и вы могли бы просто поставить под угрозу весь ваш сервер.
Может быть, это следует считать сообщаемой дырой в безопасности в модуле zipfile в Python, но любое количество архиваторов в прошлом демонстрировало точно такое же поведение. Чтобы безопасно разархивировать ZIP-файл со структурой папок, вам необходима тщательная проверка каждого пути к файлу.
Я испытал это и могу воспроизвести его. 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
; если это - каталог, создайте каталог. Иначе извлеките файл.
Обратите внимание, что 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
Замените
и
настоящими именами файлов относительно Текущий каталог. Затем просто скопируйте все это и вставьте в командную оболочку, и он создаст новый архив, готовый к работе с Python 2.6. Там есть команда zip
, которая удаляет эти записи каталога без разархивирования, но IIRC она вела себя странно в различных средах оболочки или конфигурациях zip.