Выбор случайного файла из каталога (с большим количеством файлов) в Python
У меня есть каталог с большим количеством файлов (~ 1 мил). Мне нужно выбрать случайный файл из этого каталога. Поскольку существует так много файлов, os.listdir
, естественно, заканчивается вечностью.
Есть ли способ обойти эту проблему? Может быть, каким-то образом узнать количество файлов в каталоге (без его перечисления) и выбрать n-й файл, где n генерируется случайным образом?
Файлы в каталоге называются случайным образом.
Ответы
Ответ 1
Увы, я не думаю, что есть решение вашей проблемы. Во-первых, я не знаю портативного API, который вернет вам количество записей в каталоге (без перечисления их в первую очередь). Во-вторых, я не думаю, что API возвратит вам запись каталога по номеру, а не по имени.
Таким образом, в целом, программа должна будет перечислять записи каталога O (n), чтобы получить один случайный. Тривиальный подход к определению количества записей, а затем выбор одного из них потребует достаточного количества ОЗУ для хранения полного списка (os.listdir()
) или потребуется перечислить второй раз в каталоге, чтобы найти случайный (n) элемент - общие операции n+n/2
в среднем.
Есть немного лучший подход - но только слегка - см. случайный выбор-строк-из файлов. Короче говоря, есть способ выбрать случайный элемент из списка/итератора с неизвестной длиной, одновременно читая один элемент и гарантируя, что любой предмет может быть выбран с равной вероятностью. Но это не поможет с os.listdir()
, потому что он уже возвращает list
в памяти, которая уже содержит все записи 1M +, поэтому вы можете также задать вопрос о len()
...
Ответ 2
Я не уверен, что это возможно. Даже на уровне VFS или файловой системы нет гарантии, что счетчик записей в каталогах даже поддерживается. Например, многие файловые системы просто записывают объединенный размер байта структур записи каталога, содержащихся в заданной директории.
Оценка может быть сделана, если записи в каталогах являются структурами фиксированного размера, но это редко встречается сейчас (рассмотрим LFN для FAT32). Даже если данная файловая система предоставила счетчик записей, не требуя повторения в каталоге, или если VFS кэширует запись длины каталогов, это определенно будет операционной системой, файловой системой и конкретным ядром.
Ответ 3
У меня аналогичная потребность в OP.
Я думаю, что я улажу метод префикса: вы сохраняете в TXT файле список всех файлов, тогда вы можете просто умело искать случайную строку в своем листинге (даже не загружая ее в память), и все готово!
Конечно, вам все равно нужно обновить кеш и, что еще важнее, определить , когда вам нужно обновить кеш, но в зависимости от ваших потребностей это может быть легко (сразу после определенного действия, или когда что-то изменилось и т.д.).
Код для умного чтения случайной строки из файла в Python Джонатана Купфермана:
http://www.regexprn.com/2008/11/read-random-line-in-large-file-in.html
Ответ 4
Возможно, вы сможете запустить это:
http://mail.python.org/pipermail/python-list/2009-July/1213182.html
И это, вероятно, лучшее возможное решение вашей проблемы, но только там, где n
невелик - если n
идет большим, то os.listdir, вероятно, так же хорош для вашей цели.
Я охотился и не нашел другого способа открыть файл в каталоге. Если бы у меня было больше времени, я был бы склонен немного поиграть и сгенерировать свои файлы ~ 1mil.
Я просто подумал о другом способе сделать это:
Предполагая, что файлы постоянны - вы не получаете больше или меньше - вы можете сохранить список имен файлов в базе данных sqlite. Тогда было бы относительно просто запросить базу данных для имени случайным ROWID
. Я не знаю, будет ли у вас по-прежнему долгое время искать правильный файл, но по крайней мере получение имени файла должно занимать небольшую сумму.
Конечно, если файлы в каталоге называются случайным образом, вы можете переименовать файлы (?) и поместить их в структуру каталогов, как предлагает AdamK.
Ответ 5
попробуйте это, (здесь очень быстро с файлами 50K...)
import glob
import random
list = glob.glob("*/*.*")
print list[random.randrange(0,list.__len__())]