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.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,我无法抗拒).
我已经编辑了上面的段落.
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
不幸的是,"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倍).
从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)
我想要的是一种产生文件名,处理它,然后产生下一个文件名的方法,而不是将它们全部读入内存.
没有方法会显示"已更改"的文件名.甚至不清楚这个"文件名更改,添加新文件,删除文件"是什么意思?你的用例是什么?
比方说,你有三个文件: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(),据我所知,它具有在编译时定义的缓冲区大小.