解决os.listdir中的OSError问题

12 投票
4 回答
3414 浏览
提问于 2025-04-16 06:34

我有一个文件夹,里面有9万个文件。这真是个超级庞大的数字,以至于像 ls 这样的命令都无法正常工作。我的Python脚本中的 os.listdir() 也失败了,报错信息是 OSError: [Errno 12] Cannot allocate memory: '.'

有人会说:“别把这么多文件放在一个文件夹里!你疯了吗?”——但我喜欢假装自己生活在未来,一个光辉灿烂的地方,那里有大量的内存可供我使用,我不需要太担心我的文件放在哪里,只要我的硬盘还有空间就行。

那么,有没有什么好的办法来解决这个 os.listdir() 的问题呢?我考虑过直接用 find 命令,但这有点麻烦,而且不幸的是,find 是递归的,在Mac OS X 10.6上没有支持的最大深度选项。

下面是通过调用 find 来实现的 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

更新: 在Python 2.6中,os.listdir() 可以正常工作。

4 个回答

4

你可以试着更深入一步,直接使用ctypes来调用opendir()和readdir()这两个函数。

8

你遇到了Python中的一个历史遗留问题:os.listdir应该返回一个迭代器,而不是一个数组。我觉得这个函数是在迭代器出现之前就存在的——奇怪的是,居然没有添加os.xlistdir这个函数。

这个问题不仅仅影响内存使用,尤其是在处理非常大的文件夹时。即使在一个只有几千个文件的文件夹里,你也得等整个文件夹的扫描完成,甚至需要读取所有的文件,即使你只想找第一个文件。

这在Python中是个明显的缺陷:似乎没有与低级的opendir/readdir/fdopendir这些API的绑定,所以看起来你甚至无法自己实现这个功能,除非写一个本地模块。这种情况就像是标准库中有个巨大的漏洞,让我怀疑自己是不是没看清楚——虽然有低级的openstat等绑定,但这个问题也应该属于同一类。

2
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

当然可以!请把你想要翻译的内容发给我,我会帮你把它变得更简单易懂。

撰写回答