2012-08-30 174 views
4

我正在通过包含100,000个文件的目录寻找到迭代的方法。使用os.listdir的速度很慢,因为此函数首先从整个指定路径获取路径列表。遍历目录

什么是最快的选择?

注意:谁downvoted从来没有面对这种情况肯定。

+0

http://stackoverflow.com/questions/120656/directory-listing-in-python – squiguy

+1

[列出文件夹中的文件作为流立即开始处理](http://stackoverflow.com/questions/4403598/list-files-in-a-folder-as-a-stream-to-begin-process-immediately) – Nemo

+1

@squiguy:你引用的问题与我之后的问题不一样。 – jldupont

回答

1

这另一个问题是在评论为重复简称:
List files in a folder as a stream to begin process immediately

...但我发现的例子是半不工作。这里是适用于我的固定版本:

from ctypes import CDLL, c_int, c_uint8, c_uint16, c_uint32, c_char, c_char_p, Structure, POINTER 
from ctypes.util import find_library 

import os 

class c_dir(Structure): 
    pass 

class c_dirent(Structure): 
    _fields_ = [ 
     ("d_fileno", c_uint32), 
     ("d_reclen", c_uint16), 
     ("d_type", c_uint8), 
     ("d_namlen", c_uint8), 
     ("d_name", c_char * 4096), 
     # proper way of getting platform MAX filename size? 
     # ("d_name", c_char * (os.pathconf('.', 'PC_NAME_MAX')+1)) 
    ] 

c_dirent_p = POINTER(c_dirent) 
c_dir_p = POINTER(c_dir) 

c_lib = CDLL(find_library("c")) 
opendir = c_lib.opendir 
opendir.argtypes = [c_char_p] 
opendir.restype = c_dir_p 

# FIXME Should probably use readdir_r here 
readdir = c_lib.readdir 
readdir.argtypes = [c_dir_p] 
readdir.restype = c_dirent_p 

closedir = c_lib.closedir 
closedir.argtypes = [c_dir_p] 
closedir.restype = c_int 

def listdir(path): 
    """ 
    A generator to return the names of files in the directory passed in 
    """ 
    dir_p = opendir(".") 
    try: 
     while True: 
      p = readdir(dir_p) 
      if not p: 
       break 
      name = p.contents.d_name 
      if name not in (".", ".."): 
       yield name 
    finally: 
     closedir(dir_p) 


if __name__ == "__main__": 
    for name in listdir("."): 
     print name 
+0

Works像一个魅力。谢谢! – jldupont

0

你在做什么目录中的每个文件?我认为使用os.listdir并没有什么选择,但根据你在做什么,你可能能够并行处理文件。例如,我们可以使用多处理库中的Pool来产生更多的Python进程,然后让每个进程遍历一小部分文件。

http://docs.python.org/library/multiprocessing.html

这有点粗糙,但我认为它横跨得到点...

import sys 
import os 
from processing import Pool 

p = Pool(3) 
def work(subsetOfFiles): 
    for file in subsetOfFiles: 
     with open(file, 'r') as f: 
      #read file, do work 
    return "data" 

p.map(work, [[#subSetFiles1],[#subSetFiles2],[#subSetFiles3]]) 

的总体思路是,以获得从os.listdir文件的列表,而是逐个超过100,000个文件,我们将100,000个文件分为20个5000个文件列表,并在每个过程中处理5,000个文件。这种方法的好处之一是它将受益于当前多核系统的趋势。

+0

我认为OP的问题在于调用'os.listdir'本身需要很长时间,因为该目录中的项目数量很大。所以在这种情况下,地图不会在整个列表被获取之前开始。 – jdi

+0

谢谢,我误解了一些问题。我认为,即使在这种情况下,你也可以使用我上面概述的方法。您可以让每个工作进程获取目录中文件的相同子集(可能通过直接shell调用),而不是一次获取所有文件的列表,然后再将其分配给工作进程。我只相信,当我们谈论大约10万个文件时,分而治之是一个好方法,并且由于全局解释器锁定,您将通过流程来执行此操作。 – Wulfram

+0

磁盘IO通常不是GIL的问题,所以线程仍然很好,我相信。在系统阻塞呼叫期间,GIL不会被占用。但即使是分而治之的方法......如何提前将目录中的文件分开?无论如何,一个目录列表必须发生,这又是持久性的。你在工作方面所做的事情实际上是第二步。 – jdi