在Python中从目录(包含大量文件)中选择随机文件

在Python中从目录(包含大量文件)中选择随机文件,python,file,Python,File,我有一个包含大量文件的目录(~1mil)。我需要从这个目录中选择一个随机文件。由于文件太多,os.listdir自然要花很长时间才能完成 有什么办法可以绕过这个问题吗?也许可以知道目录中的文件数(不列出),然后选择随机生成n的第n个文件 目录中的文件是随机命名的。我甚至不确定这是否可行。即使在VFS或文件系统级别,也不能保证维护目录条目计数。例如,许多文件系统只是记录给定目录中包含的目录项结构的组合字节大小 如果目录项是固定大小的结构,则可以进行估计,但现在这种情况并不常见(考虑FAT32的LF

我有一个包含大量文件的目录(~1mil)。我需要从这个目录中选择一个随机文件。由于文件太多,
os.listdir
自然要花很长时间才能完成

有什么办法可以绕过这个问题吗?也许可以知道目录中的文件数(不列出),然后选择随机生成n的第n个文件


目录中的文件是随机命名的。

我甚至不确定这是否可行。即使在VFS或文件系统级别,也不能保证维护目录条目计数。例如,许多文件系统只是记录给定目录中包含的目录项结构的组合字节大小


如果目录项是固定大小的结构,则可以进行估计,但现在这种情况并不常见(考虑FAT32的LFN)。即使给定的文件系统确实提供了一个条目计数,而不需要遍历目录,或者如果VFS缓存了一个目录长度的记录,这些记录肯定是特定于操作系统、文件系统和内核的。

您可能能够运行:

这可能是解决您的问题的最佳解决方案,但只有在
n
较小的情况下-如果
n
变大,那么os.listdir可能对您的目的同样有效

我四处搜寻,没有找到任何其他方法来打开目录中的文件。如果我有更多的时间,我会倾向于玩一玩,生成自己的~1mil文件


我只是想到了另一种方法: 假设这些文件是常量-您不会得到更多或更少-您可以在sqlite数据库中保留一个文件名列表。然后,通过随机的
ROWID
查询数据库中的名称将相对简单。我不知道你是否仍然会被长时间搜索正确的文件所困扰,但至少获得一个文件名需要很短的时间

当然,如果目录中的文件是随机命名的,您可以重命名这些文件(?)并将它们放入AdamK建议的目录结构中。

试试这个(这里有50K文件非常快速…)


唉,我认为你的问题没有解决办法。首先,我不知道有哪种可移植API会返回目录中的条目数(不首先枚举它们)。第二,我认为没有API可以按编号而不是名称返回目录条目

因此,总体而言,一个程序必须枚举O(n)个目录条目才能得到一个随机条目。确定条目数量然后选择一个条目的简单方法要么需要足够的RAM来保存完整的列表(
os.listdir()
),要么需要第二次枚举目录以查找随机(n)项-总体平均
n+n/2
操作


有一种稍微好一点的方法——但只是稍微好一点——见。简言之,有一种方法可以从长度未知的列表/迭代器中选择随机项,同时一次读取一个项,并确保以相同的概率选择任何项。但是这对
os.listdir()
没有帮助,因为它已经在内存中返回了
list
,内存中已经包含了所有1M+项,所以你也可以询问它关于
len()

我对OP有类似的需求

我想我会采用一种预编译的方法:你将所有文件的列表存储在一个.txt文件中,然后你就可以在列表中巧妙地随机搜索一行(甚至不必在内存中加载),你就完成了

当然,您仍然需要更新缓存,更重要的是定义何时需要更新缓存,但根据您的需要,这可能很容易(仅在特定操作之后,或当某些内容发生更改时,等等)

Jonathan Kupferman用Python从文件中巧妙地读取随机行的代码:


您运行的是什么操作系统?2.6.30.10.1.amd64-smp#1 x86(u 64 GNU/Linux)您是否控制目录中文件的名称?纯粹的好奇:您是如何在一个目录中获得~1mil符号链接的,为什么你需要一个随机的?也许在那个目录中维护一个包含所有文件名列表的文件如果目录中的所有文件都是符号链接会有帮助吗?在我的系统中,所有这些链接的大小都是512B。因此,我们是否可以使用此信息和合并的目录大小信息提取文件数?我很有希望我是错的,我很希望看到您的问题的一个很好的技术答案。这需要花费同样多的时间。请注意
random.randrange(0,list.\u len\u())
最好写成
random.randrange(len(list))
这是一个好主意,我很想用Wayne建议的
os.listdir
生成器函数来试试。@NoneType:如果你想玩它,当然可以。但我不认为仅仅提高2倍就值得付出努力;你应该争取线性或对数。尽管你应该能够以某种方式改变这个问题。。。你到底为什么需要随机选择文件,它背后的需求是什么?您对文件命名模式有更好的了解吗?我将尝试使用nasbanov建议的
listdir
generator函数和随机抽样启发式方法。(即,在逐个读取文件名时,对所有文件名进行统一采样)
import glob
import random

list = glob.glob("*/*.*")
print list[random.randrange(0,list.__len__())]