问题 使用os.listdir解决OSError问题


我有一个包含90K文件的目录。这是一个非常庞大的文件,可以像bash一样使用 ls 失败。当然可以 os.listdir() 来自我的python(Mac Python,版本2.5)脚本;它失败了 OSError: [Errno 12] Cannot allocate memory: '.'

人们会说“不要把那么多文件放在一个目录里!你疯了吗?” - 但我喜欢假装我生活在未来,一个辉煌,发光的地方,我可以随意使用千兆字节的内存,而且不需要太担心我的文件到底在哪里,只要有我的旋转盘片上留下了锈迹。

那么,有没有一个很好的解决方法 os.listdir() 问题?我考虑过只是炮轰 find,但不幸的是,这有点严重 find 是递归的,在Mac OS X 10.6上没有受支持的maxdepth选项。

这是os.listdir通过shell来查找的内容,粗略地说:

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中取得成功。


4247
2017-11-04 16:25


起源

你得到的错误是什么?我经常使用 os.listdir 获取包含50-100k文件的目录列表。 os.listdir 也是最快的选择,因为它没有 stat 目录中的每个文件。 - Seth
@Seth,我得到了OSError:[Errno 12]无法分配内存:'。' - Jason Sundram
此目录中大约有多少个文件? - Garrett Hyde
@Garrett得到了A +阅读理解。 d: - Glenn Maynard
+1雄辩地把它:) People will say "Don't put that many files in a single directory! Are you crazy?" -- but I like to pretend that I live in the future, a brilliant, glowing place, where I have gigabytes of memory at my disposal, and don't need to worry too much about where exactly my files go, as long as there's rust left on my spinning platters. - Watt


答案:


你在Python中遇到了一个历史神器: os.listdir 应该返回一个迭代器,而不是一个数组。我认为这个函数早于迭代器 - 奇怪的是没有 os.xlistdir 已被添加。

这比在巨大的目录上使用内存更有效。即使在只有几千个文件的目录上,您也必须等待整个目录扫描完成,并且您必须阅读 整个 目录,即使第一个条目是您要查找的条目。

这在Python中是一个相当明显的缺点:似乎有 没有 绑定到低级别 opendir/readdir/fdopendir API,所以看起来如果不编写本机模块就不可能自己实现它。这是标准库中如此庞大,空洞的漏洞之一,我怀疑自己并怀疑我只是没有看到它 - 有低级别 openstat等等绑定,这属于同一类别。


7
2017-11-04 16:46



总是很高兴看到神秘的downvotes。我猜这是“敢于批评Python”的惩罚。 - Glenn Maynard
ImportError: No module named criticize - Seth
这意味着 ImportError: No module named improvement。 - Glenn Maynard
或者你因为没有回答“有没有一个好的解决办法?”而被低估了?题。 - Russell Borogove
@Russell:我对此的回答非常简单:因为没有 opendir 绑定,没有编写本机模块,这个问题没有解决方法。 (当然,我知道很多人会在没有费心阅读的情况下投票。) - Glenn Maynard


您可以尝试更深入一级,并使用ctypes直接调用opendir()和readdir()。


4
2017-11-04 16:43



绝望,但如果 listdir 和 ls 失败是我唯一能想到的。 - bobince
这很诱人,但问题在于 readdir 将数据作为a返回 struct direct,其布局是特定于平台的。我认为唯一 可靠 实现这一点的方法是使用本机模块。 - Glenn Maynard


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
2017-11-04 16:33



什么是downvotes,你们都? - Jason Sundram
是的,它可以工作,但无法帮助解决90k文件的性能问题。 - Terrel Shumway
我只是在找一些有用的东西 - 不关心性能。无效的高性能解决方案不是解决方案。 - Jason Sundram


列出一个大目录时,我在10.6上的Apple Python 2.5.5上得到了相同的IOError。它在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在不应该的情况下随机失败“和”Posix的listdir()中的错误检查”。


2
2017-11-04 20:05