Обходной путь OSError с os.listdir
У меня есть каталог с 90K файлами. Это такое невероятно огромное количество файлов, которые bash функционируют как ls
сбой. Так что, конечно, os.listdir()
из моего python (Mac Python, версия 2.5) script; он терпит неудачу с OSError: [Errno 12] Cannot allocate memory: '.'
Люди скажут: "Не ставьте много файлов в один каталог! Вы с ума сошли?" - но мне нравится притворяться, что я живу в будущем, блестящее, светящееся место, где у меня есть гигабайты памяти в моем распоряжении, и не нужно слишком беспокоиться о том, куда именно идут мои файлы, пока есть ржавчина осталась на моих вращающихся пластинах.
Итак, есть ли хороший обход для этой проблемы os.listdir()
? Я считал, что просто обходится без find
, но это немного грубо и, к сожалению, find
является рекурсивным, без поддержки параметра maxdepth в Mac OS X 10.6.
Здесь то, что выглядит os.listdir через обход, чтобы выглядеть, грубо:
def ls(directory):
import os
files = os.popen4('find %s' % directory)[1].read().rstrip().split('\n')
files.remove(directory)
return files # probably want to remove dir prefix from everything in here too
Обновление: os.listdir()
преуспевает в python 2.6.
Ответы
Ответ 1
def ls(directory):
"""full-featured solution, via wrapping find"""
import os
files = os.popen4('find %s' % directory)[1].read().rstrip().split('\n')
files.remove(directory)
n = len(directory)
if directory[-1] != os.path.sep:
n += 1
files = [f[n:] for f in files] # remove dir prefix
return [f for f in files if os.path.sep not in f] # remove files in sub-directories
Ответ 2
Вы поражаете исторический артефакт в Python: os.listdir
должен возвращать итератор, а не массив. Я думаю, что эта функция предшествует итераторам - странно, что не было добавлено os.xlistdir
.
У этого есть больше эффектов, чем использование памяти в огромных каталогах. Даже в каталоге с несколькими тысячами файлов вам придется ждать завершения полного сканирования каталога, и вы должны прочитать весь каталог, даже если первая запись - это тот, который вы искали.
Это довольно вопиющий недостаток в Python: похоже, нет привязки к низкоуровневым API-интерфейсам opendir
/readdir
/fdopendir
, поэтому кажется, что это даже невозможно реализовать самостоятельно без написания собственный модуль. Это один из тех случаев, когда у меня такая огромная, зияющая дыра в стандартной библиотеке, что я сомневаюсь в себе и подозреваю, что я просто не вижу ее - существуют привязки уровня open
, stat
и т.д. и это в той же категории.
Ответ 3
Вы можете попробовать перейти на один уровень глубже и напрямую вызвать opendir() и readdir() с помощью ctypes.
Ответ 4
Я получаю тот же IOError на Apple Python 2.5.5 на 10.6 при перечислении большого каталога. Он отлично работает в Python2.6.
Python 2.5.5 (r255:77872, Sep 21 2010, 09:52:31)
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> x = os.listdir('.')
OSError: [Errno 12] Cannot allocate memory: '.'
Кажется, это ошибка в Python2.5. См. " os.listdir случайным образом терпит неудачу в случаях, когда это не должно быть" и " Неверная проверка ошибок в listdir() для Posix".