我知道os.listdir
,但据我所知,它将目录中的所有文件名都存入内存,然后返回列表。我想要的是一种产生文件名的方法,对它进行处理,然后产生下一个文件名,而不是将它们全部读入内存。
有没有办法做到这一点?我担心文件名更改,添加新文件以及使用此类方法删除文件的情况。一些迭代器阻止您在迭代期间修改集合,主要是通过在开始时获取集合状态的快照,并在每个move
操作上比较该状态。如果有一个迭代器能够从路径中产生文件名,那么如果有文件系统更改(添加,删除,重命名迭代目录中的文件)修改集合会引发错误吗?
可能有一些情况可能导致迭代器失败,这一切都取决于迭代器如何维持状态。使用S.Lotts示例:
filea.txt
fileb.txt
filec.txt
迭代器产生filea.txt
。在processing
期间,filea.txt
重命名为filey.txt
,fileb.txt
重命名为filez.txt
。当迭代器尝试获取下一个文件时,如果要使用文件名filea.txt
找到它的当前位置以便找到下一个文件并且filea.txt
不存在,会发生什么?它可能无法恢复它在集合中的位置。同样,如果迭代器在产生fileb.txt
时要获取filea.txt
,它可能会查找fileb.txt
的位置,失败并产生错误。
如果迭代器能够以某种方式维护索引dir.get_file(0)
,那么维护位置状态不会受到影响,但是某些文件可能会被遗漏,因为它们的索引可以被移动到迭代器“后面”的索引
这当然是理论上的,因为似乎没有内置(python)方法来迭代目录中的文件。但是,下面有一些很好的答案可以通过使用队列和通知来解决问题。
编辑:
关注的操作系统是Redhat。我的用例是:
进程A不断将文件写入存储位置。 进程B(我正在写的那个)将迭代这些文件,根据文件名进行一些处理,并将文件移动到另一个位置。
编辑:
有效的定义:
形容词 1.良好的基础或合理的,相关的。
(对不起S.Lott,我无法抗拒)。
我已经编辑了上面的段落。
答案 0 :(得分:13)
tl; dr< update>:从Python 3.5开始(目前处于测试阶段),只需使用os.scandir
< /更新>
正如我之前所写,由于“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
不幸的是,“dirent64”C结构是在每个系统的C编译时确定的 - 我已经在我的系统上想到了这一点,而且在大多数情况下,它就像我把它放在Python下面的片段中 - 但是你可能会想要知道你的“dirent.h”以及/ usr / includes中包含的其他文件。
以下是使用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
更新:Python 3.5现在处于测试阶段 - 在此版本中,新的os.scandir
函数调用可用作PEP 471的实现(“更好更快的目录iterator“)完全符合这里的要求,除了很多其他优化之外,在Windows下的大目录列表中,os.listdir
可以提供高达9倍的速度增加(Posix系统增加2-3倍)。
答案 1 :(得分: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.
例如:
import glob
for eachfile in glob.iglob('*'):
# act upon eachfile
答案 2 :(得分:8)
由于您使用的是Linux,因此您可能需要查看pyinotify。 它允许您编写一个Python脚本来监视目录中的文件系统更改 - 例如创建,修改或删除文件。
每次发生此类文件系统事件时,您都可以安排Python脚本调用函数。这大致就像产生每个文件名一次,同时能够对修改和删除作出反应。
听起来你已经有一百万个文件放在一个目录中了。在这种情况下,如果要将所有这些文件移动到新的pyinotify监视目录,则通过创建新文件生成的文件系统事件将根据需要生成文件名。
答案 3 :(得分: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从队列中读取纪念品,然后对纪念品中指定的文件进行后续处理。
答案 4 :(得分:6)
@jsbueno的帖子非常有用,但在慢速磁盘上仍然有点慢,因为libc readdir()一次只能准备32K磁盘条目。我不是直接在python中进行系统调用的专家,但我概述了如何在C中编写代码,该代码将列出包含数百万个文件的目录,位于http://www.olark.com/spw/2011/08/you-can-list-a-directory-with-8-million-files-but-not-with-ls/的博客文章中。
理想情况是直接在python( http://www.kernel.org/doc/man-pages/online/pages/man2/getdents.2.html中调用getdents()),这样就可以在从磁盘加载目录条目时指定读取缓冲区大小。
而不是调用readdir(),据我所知,它在编译时定义了一个缓冲区大小。
答案 5 :(得分:1)
我认为由于文件IO的性质,你所要求的是不可能的。一旦python检索到目录列表,它就无法维护磁盘上实际目录的视图,也没有办法让python坚持要求操作系统通知它对目录的任何修改。
所有python可以做的是询问定期列表并对结果进行区分以查看是否有任何更改。
您可以做的最好的事情是在目录中创建一个信号量文件,让其他进程知道您的python进程希望没有其他进程修改目录。当然,如果你明确地将它们编程为它们,它们只会观察信号量。