有没有办法有效地产生包含数百万个文件的目录中的每个文件?

Jos*_*ton 12 python yield file list

我知道os.listdir,但据我所知,它将目录中的所有文件名都存入内存,然后返回列表.我想要的是一种产生文件名,处理它,然后产生下一个文件名的方法,而不是将它们全部读入内存.

有没有办法做到这一点?我担心文件名更改,添加新文件以及使用此类方法删除文件的情况.一些迭代器阻止您在迭代期间修改集合,主要是通过在开始时获取集合状态的快照,并在每个move操作上比较该状态.如果有一个迭代器能够从路径中产生文件名,那么如果有文件系统更改(添加,删除,重命名迭代目录中的文件)修改集合会引发错误吗?

可能有一些情况可能导致迭代器失败,这一切都取决于迭代器如何维持状态.使用S.Lotts示例:

filea.txt
fileb.txt
filec.txt
Run Code Online (Sandbox Code Playgroud)

迭代器产量filea.txt.期间processing,filea.txt被重命名为filey.txtfileb.txt被重命名为filez.txt.当迭代器试图获取下一个文件时,如果要使用文件名filea.txt找到它的当前位置以便找到下一个文件filea.txt而不存在,会发生什么?它可能无法恢复它在集合中的位置.类似地,如果迭代器fileb.txt在屈服时要获取filea.txt,它可能会查找fileb.txt,失败并产生错误的位置.

如果迭代器能够以某种方式维护索引dir.get_file(0),那么维护位置状态不会受到影响,但是一些文件可能会被遗漏,因为它们的索引可以被移动到迭代器"后面"的索引.

这当然是理论上的,因为似乎没有内置(python)方法迭代目录中的文件.但是,下面有一些很好的答案可以通过使用队列和通知来解决问题.

编辑:

关注的操作系统是Redhat.我的用例是这样的:

进程A不断将文件写入存储位置.进程B(我正在写的那个)将迭代这些文件,根据文件名进行一些处理,并将文件移动到另一个位置.

编辑:

有效的定义:

形容词1.良好的基础或合理的,相关的.

(对不起S.Lott,我无法抗拒).

我已经编辑了上面的段落.

jsb*_*eno 13

tl; dr <update>:从Python 3.5(目前处于测试版)开始,只需使用os.scandir </ update>

正如我之前所写,由于"iglob"只是一个真正的迭代器的外观,你必须调用低级系统函数,以便一次一个地得到一个.Fortyuantelly,这可以从Python实现.如果没有告诉你,你是在Posix(Linux/mac OS X /其他Unix)或Windows系统上.在后一种情况下,您应该检查win32api是否有任何调用来读取"dir的下一个条目"或者如何继续.

在前一种情况下,您可以直接通过ctypes调用libc函数,并获取一个文件目录条目,包括命名信息).

关于C函数的文档在这里:http: //www.gnu.org/s/libc/manual/html_node/Opening-a-Directory.html#Opening-a-Directory

http://www.gnu.org/s/libc/manual/html_node/Reading_002fClosing-Directory.html#Reading_002fClosing-Directory

不幸的是,"dirent64"C结构是在每个系统的C编译时确定的 - 我在我的系统上已经想到了,而且在大多数情况下,它就像我把它放在Python上的片段下面 - 但你可能想要checj你的"dirent.h"和其他包含在/ usr/includes下的fiels.

这是使用ctypes和libC的片段,我将它放在一起,允许您获取每个文件名,并对其执行操作.请注意,当您对结构上定义的char数组执行str(...)时,ctypes会自动为您提供Python字符串.(我使用的是print语句,它隐式调用Python的str)

from ctypes import *
libc = cdll.LoadLibrary( "libc.so.6")
 dir_ = c_voidp( libc.opendir("/home/jsbueno"))

class Dirent(Structure):
    _fields_ = [("d_ino",  c_voidp),
                ("off_t", c_int64),
                ("d_reclen", c_ushort),
                ("d_type", c_ubyte),
                ("d_name", c_char * 2048)
            ]

while True:
    p  = libc.readdir64(dir_)
    if not p:
        break
    entry = Dirent.from_address( p)
    print entry.d_name
Run Code Online (Sandbox Code Playgroud)

更新:Python 3.5现在处于测试阶段 - 在这个版本中,新的os.scandir函数调用可以作为PEP 471的实现("更好更快的目录迭代器"),它完全符合这里的要求,除了很多其他优化之外os.listdir在Windows下的大目录列表中,可以提供高达9倍的速度提升(Posix系统增加2-3倍).


Sen*_*ran 9

从2.5开始,glob模块Python有一个返回迭代器的iglob方法.迭代器完全是为了不在内存中存储巨大的值.

glob.iglob(pathname)
Return an iterator which yields the same values as glob() without
actually storing them all simultaneously.
Run Code Online (Sandbox Code Playgroud)

例如:

import glob
for eachfile in glob.iglob('*'):
    # act upon eachfile
Run Code Online (Sandbox Code Playgroud)

  • iglob似乎是glob.glob1的生成器包装器,它返回一个列表.所以整个列表仍然加载到内存中. (5认同)

unu*_*tbu 8

由于您使用的是Linux,因此您可能需要查看pyinotify.它允许您编写一个Python脚本来监视目录以查找文件系统更改 - 例如创建,修改或删除文件.

每次发生这样的文件系统事件时,您都可以安排Python脚本来调用函数.这大致就像产生每个文件名一次,同时能够对修改和删除作出反应.

听起来你已经有一百万个文件放在一个目录中了.在这种情况下,如果要将所有这些文件移动到新的pyinotify监视目录,则通过创建新文件生成的文件系统事件将根据需要生成文件名.


S.L*_*ott 6

我想要的是一种产生文件名,处理它,然后产生下一个文件名的方法,而不是将它们全部读入内存.

没有方法会显示"已更改"的文件名.甚至不清楚这个"文件名更改,添加新文件,删除文件"是什么意思?你的用例是什么?

比方说,你有三个文件:a.a,b.b,c.c.

你的神奇"迭代器"开头a.a.你处理它.

神奇的"迭代器"移动到了b.b.你正在处理它.

同时a.a被复制到a1.a1,a.a被删除.现在怎么办?你的魔法迭代器对这些做了什么?它已经过去了a.a.既然a1.a1是之前b.b,它永远不会看到它."文件名更改,添加新文件,删除文件"应该会发生什么?

神奇的"迭代器"移动到了c.c.应该发生在其他文件上的是什么?你怎么知道删除的?


进程A不断将文件写入存储位置.进程B(我正在写的那个)将迭代这些文件,根据文件名进行一些处理,并将文件移动到另一个位置.

不要使用裸文件系统进行协调.

使用队列.

进程A写入文件并将添加/更改/删除纪念品排入队列.

进程B从队列中读取纪念品,然后对纪念品中指定的文件进行后续处理.


小智 6

@jsbueno的帖子非常有用,但是在慢速磁盘上仍然有点慢,因为libc readdir()一次只能准备好32K的磁盘条目.我不是直接在python中进行系统调用的专家,但我概述了如何在C中编写代码,列出包含数百万个文件的目录,在博客文章中:http://www.olark.com/spw/ 2011/08/you-can-list-a-directory-with-eight-files-but-not-with-ls /.

理想的情况是直接在python中调用getdents()(http://www.kernel.org/doc/man-pages/online/pages/man2/getdents.2.html)因此您可以在从磁盘加载目录条目时指定读取缓冲区大小.

而不是调用readdir(),据我所知,它具有在编译时定义的缓冲区大小.