【问题标题】:Most efficient/fastest way to get a single file from a directory从目录中获取单个文件的最有效/最快的方法
【发布时间】:2012-11-14 11:57:36
【问题描述】:

使用 Python 从目录中获取单个文件最有效和最快的方法是什么?

关于我的具体问题的更多详细信息:
我有一个包含很多预生成文件的目录,我只想随机选择一个。因为我知道除了首先列出所有文件之外没有真正有效的从目录中选择随机文件的方法,所以我的文件是用一个已经随机的名称生成的,因此它们已经是随机排序的,我只需要选择第一个文件夹中的文件。

所以我的问题是:如何从我的文件夹中选择第一个文件,而不必从目录中加载整个文件列表(也没有操作系统来执行此操作,我的最佳目标是强制操作系统只需返回一个文件,然后停止!)。

注意:我的目录中有很多文件,因此我不想列出所有文件而只选择一个。

注意2:每个文件只选择一次,然后删除以确保下次只选择新文件(从而确保某种随机性)。

解决方案

我最终选择使用一个索引文件来存储:

  • 要选取的当前文件的索引(例如:1 代表 file1.ext,2 代表 file2.ext,等等)
  • 最后生成的文件的索引(例如:file1999.ext 为 1999)

当然,这意味着我的文件不再使用随机名称生成,而是使用确定性可递增模式(例如:“file%s.ext”% ID)

因此,我的两个主要操作的时间几乎恒定:

  • 访问文件夹中的下一个文件
  • 计算剩余文件的数量(以便我可以在需要时在后台线程中生成新文件)。

这是针对我的问题的具体解决方案,更多通用解决方案,请阅读接受的答案。

您可能还对我发现的另外两个解决方案感兴趣,这些解决方案可以使用 Python 优化文件访问和目录遍历:

【问题讨论】:

  • 弄清楚一次,然后建立自己的数据库以使其更快?
  • 目录是不排序的。相反,它们以当前目录结构顺序(磁盘结构)列出项目,可能是创建顺序。
  • 我避免使用 DBMS 是有原因的,因为我不需要任何类型的索引。我不想选择一个特定的文件,只是任何文件,而且只有一个。 @Martijn:我所说的是为了更好地传达我的想法而进行的简化。文件按 inode 排序,但这不是重点:我只想获取第一个文件,因为我的文件已经是随机的,所以我不必关心随机选择文件:只选择此目录中的任何文件都已经给我一个随机结果。
  • 我什至不确定操作系统是否可以从目录中获取给定名称的文件,而不在内部列出目录中的文件。
  • POSIX具体解决方案是通过ctypes使用opendir()/readdir()系统函数。

标签: python file directory


【解决方案1】:

1 个目录中没有很多预生成的文件。如果目录中的文件超过“n”个,则将它们划分为子目录。

【讨论】:

  • 这不是一个解决方案:我仍然需要列出所有文件夹,然后列出我将访问的子目录中的所有文件,以了解还有多少预生成的文件可供选择删除文件夹与否。如果我不这样做,从长远来看,我会得到很多空文件夹,我仍然会走路并浪费 CPU 周期。
  • 不正确。列出文件夹不会花费太多时间,因为只列出 n 个文件夹。然后你从一个文件夹到另一个文件夹,直到你找到一个文件。使用后删除文件并检查文件夹是否为空。如果是这样,请删除该文件夹。递归执行此操作,您将大大减少列出的文件数量。 (我的意思是您递归地在文件夹中划分文件,因此文件夹也可以包含子文件夹。)如果我是正确的,这会将您列出的文件从 O(N) 减少到 O(n-root(N)) .
  • 好的,我明白你的意思。事实上,如果我们把这个想法发挥到极致,它是一种递归算法,但是对于文件放置而不是数据处理:为了最大限度地发挥它的潜力,我们可以将每个文件放在自己的文件夹中(所以每个文件夹一个文件!)并且每 2 个文件夹递归地放置在一个文件夹中。这样,我们将创建一个具有 log2(N) 个杠杆的树(根为 N/2**log2(N))。复杂性类似于 O(n*log2(N)),但我们必须使用主定理来获得更精确的结果。 (在下一条评论中继续)
  • 这确实是在没有任何缓存的情况下直接访问文件的最快方式,但有一个很大的限制:文件系统通常接受有限深度的文件夹递归,因此该系统不能真正直接在一个文件系统。因此,我们应该在我们自己的应用程序中虚拟化文件夹层次结构,但是我们也可以使用某种缓存,然后在这种情况下,还有其他更好的方法可以通过使用索引来访问单个文件,就像 DBMSes 一样。
  • 这并不能完全回答这个问题,但这是一个非常好的主意。我会把它记在我的书里。
【解决方案2】:

创建文件时,将最新文件的名称添加到存储在文本文件中的列表中。当您要读取/处理/删除文件时:

  1. 打开文本文件
  2. 将文件名设置为列表顶部的名称。
  3. 从列表顶部删除名称
  4. 关闭文本文件
  5. 处理文件名。

【讨论】:

  • 不错的解决方案。事实上,鉴于我对问题的描述,这是最好的。我们还可以增强它:在文件生成时在文件末尾而不是顶部附加新名称(避免打开和读取整个文件),在文件访问时只需读取第一行并将其从文本文件中删除(我'确定有办法做到这一点,而不必阅读整个文件)。我将此解决方案标记为已接受的答案,尽管解决方案 given by Lucasmus is also pretty clever.
  • 在我的最终解决方案中,我将实现索引概念的另一种变体:我不会随机生成名称,而是使用具有递增编号的确定性模式(例如:file1.ext、file2.ext 、file3.ext 等)。然后我将保留一个索引文件,该文件将存储 2 个字段:当前 ID(例如:file1.ext 为 1)和最新生成的文件的最后一个 ID(例如:file1999.ext 为 1999)。这有几个优点:索引文件总是很短;我总是可以直接访问一个新文件;最后我还可以计算剩下的文件数而不列出文件
  • 通过计算 latestID - currentID,它允许我运行一个后台线程,当我们在指定阈值下运行时生成新文件。最后,这两个操作的访问时间几乎是恒定的:访问文件和计算剩余文件的数量。缺点是我必须小心并发访问索引文件和生成的文件以避免不同步(作为故障保护,我可以在遇到异常时重新生成它 try: except: block)。
【解决方案3】:

只需在os.listdir() 结果上使用random.choice():

import random
import os

randomfilename = random.choice(os.listdir(path_to_directory))

os.listdir() 按操作系统给出的顺序返回结果。使用随机文件名不会更改该顺序,只有在目录中添加项目或从目录中删除项目会影响该顺序。

如果您担心文件过多,请不要使用单个目录。相反,使用预先生成的名称设置目录树,随机选择其中一个,然后从那里选择一个文件。

【讨论】:

  • 如我所说,我的文件已经是随机生成的,所以没有必要使用random.choice()。此外,您的解决方案仍然列出目录中的所有文件,如果我有大量文件(这是我的情况),这将非常慢。
  • @user1121352:使用随机文件名不会创建随机顺序。
  • 我知道,但是随机文件名+第一次访问后删除文件会生成随机顺序。有点扭曲sampling without replacement
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-23
  • 1970-01-01
相关资源
最近更新 更多