【问题标题】:fast folder size calculation in Python on WindowsWindows 上 Python 中的快速文件夹大小计算
【发布时间】:2010-12-31 12:31:55
【问题描述】:

我正在寻找一种在 Windows 上用 Python 计算文件夹大小的快速方法。这是我目前所拥有的:

def get_dir_size(path):
  total_size = 0
  if platform.system() == 'Windows':
    try:
      items = win32file.FindFilesW(path + '\\*')
    except Exception, err:
      return 0

    # Add the size or perform recursion on folders.
    for item in items:
      attr = item[0]
      name = item[-2]
      size = item[5]

      if (attr & win32con.FILE_ATTRIBUTE_DIRECTORY) and \
         not (attr & win32con.FILE_ATTRIBUTE_SYSTEM):  # skip system dirs
        if name not in DIR_EXCLUDES:
          total_size += get_dir_size("%s\\%s" % (path, name))

      total_size += size

  return total_size

当文件夹大小超过 100G 时,这还不够好。任何想法如何改进它?

在一台快速机器(2Ghz+ - 5G 内存)上,226,001 个文件和 12,043 个文件夹中超过 422GB 需要 72 秒。使用资源管理器属性选项需要 40 秒。

我知道我有点贪心,但我希望有更好的解决方案。

劳伦特·卢斯

【问题讨论】:

  • 文件夹大小超过100G会怎样?你得到一个错误的答案,它是否完全失败,是否需要太长时间,还是什么?
  • 在一台速度很快的机器(2Ghz+ - 5M 内存)上,226,001 个文件和 12,043 个文件夹中超过 422GB 需要 72 秒。我知道我有点贪心,但我希望有更好的解决方案。
  • @Laurent,最好编辑您的问题以包含这些统计信息。这对于帮助人们有效回答至关重要。您还可以报告使用资源管理器的属性对话框和“du”实用程序(例如来自 Unxutils)进行比较的时间。
  • dir /s > NUL 在同一个文件夹上快很多吗?
  • 这听起来很像我对如此大量文件的期望。是什么让你认为它可以做得更快? Windows 的内置文件大小计数器是否明显更快?

标签: python windows size directory


【解决方案1】:

如果您使用 os.walk,则不需要使用递归算法。 Please check this question.

你应该对这两种方法都计时,但这应该更快:

import os

def get_dir_size(root):
    size = 0
    for path, dirs, files in os.walk(root):
        for f in files:
            size +=  os.path.getsize( os.path.join( path, f ) )
    return size

【讨论】:

  • 您提出的方法需要 139 秒而不是 72 秒。我们以前使用过那个,但速度要慢得多。
  • 那么,您已经获得了几乎 100% 的加速,但您仍然不满意吗?贪得无厌! ;-) ;-)
  • 我提出的方法是否可能运行速度较慢,因为它正在遍历所有文件,而您的方法正在跳过列表 DIR_EXCLUDES 中的系统目录和文件夹?
  • 您可以尝试使用 map 和 reduce,而不是使用 for 循环。对于数千个文件,性能优势可能非常显着。
  • @jbochi,90% 以上的时间都花在了访问文件系统上,所以使用 map() 之类的东西不太可能看到很大的改进。
【解决方案2】:

我目前没有要测试的 Windows 机器,但文档指出 win32file.FindFilesIterator 是“类似于win32file.FindFiles,但要避免为大目录创建列表”。这有帮助吗?

【讨论】:

  • 这里没有。实际上,速度稍微慢一些,考虑到列表是用 C 代码构建的,并且扫描列表的开销应该比使用迭代器少。
【解决方案3】:

它是一个目录树的庞然大物。正如其他人所说,我不确定您是否可以加快速度……不是那样,冷无数据。这意味着......

如果您可以以某种方式缓存数据(不确定实际含义是什么),那么您可以加快速度(我认为...一如既往,测量、测量、测量)。

我想我不必告诉你如何进行缓存,我猜,你看起来是个知识渊博的人。反正我也不知道 Windows 的袖手旁观。 ;-)

【讨论】:

  • 我们将采用的方法是在应用程序的后台计算文件夹大小,以便在用户请求时准备好它们。
【解决方案4】:

这让我大吃一惊:

try:
  items = win32file.FindFilesW(path + '\\*')
except Exception, err:
  return 0

异常处理会为您的算法增加大量时间。如果您可以以不同的方式指定路径,以一种您始终知道是安全的方式,从而避免捕获异常的需要(例如,在该文件夹中查找文件之前先检查给定路径是否是一个文件夹),您可以发现显着的加速。

【讨论】:

  • 实际上,Python 中的 try/except 块(与其他一些语言的经验相反)在不引发异常时非常便宜,并且无论如何该代码可以捕获无法确定的问题提前(例如某些项目上的“访问被拒绝”),因此无法避免。
  • 在文件系统中执行操作之前,您永远无法检查所有内容。除非文件系统开始支持事务(例如在 SQL 中),否则有人可以在您检查目录后一纳秒删除该目录,然后在您开始使用它之前再删除一纳秒!永远不要跳过文件系统操作的错误/异常处理!
【解决方案5】:

对代码的快速分析表明,仅FindFilesW() 调用就消耗了超过 90% 的时间。这意味着通过调整 Python 代码进行的任何改进都是次要的。

微小的调整(如果你坚持使用 FindFilesW)可能包括确保 DIR_EXCLUDES 是一个集合而不是一个列表,避免在其他模块上重复查找,以及懒惰地索引到 item[],以及移动 sys.platform检查外面。这包含了这些更改和其他更改,但不会提供超过 1-2% 的加速

DIR_EXCLUDES = set(['.', '..'])
MASK = win32con.FILE_ATTRIBUTE_DIRECTORY | win32con.FILE_ATTRIBUTE_SYSTEM
REQUIRED = win32con.FILE_ATTRIBUTE_DIRECTORY
FindFilesW = win32file.FindFilesW

def get_dir_size(path):
    total_size = 0
    try:
        items = FindFilesW(path + r'\*')
    except pywintypes.error, ex:
        return total_size

    for item in items:
        total_size += item[5]
        if (item[0] & MASK == REQUIRED):
            name = item[8]
            if name not in DIR_EXCLUDES:
                total_size += get_dir_size(path + '\\' + name)

    return total_size

唯一的显着加速将来自使用不同的 API 或不同的技术。您在评论中提到在后台执行此操作,因此您可以构建它以使用其中一个包进行增量更新以监视文件夹中的更改。可能是FindFirstChangeNotification API 或类似的东西。您可以设置为监视整个树,或者根据该例程的工作方式(我没有使用它),您最好在整个树的各个子集上注册多个请求,如果这样可以减少您的搜索量去做(当收到通知时)弄清楚实际发生了什么变化以及它现在的大小。

编辑:我在评论中询问您是否考虑了 Windows XP 及更高版本所做的大量文件系统元数据缓存。我刚刚检查了你的代码(和我的)对 Windows 本身的性能,选择了我的 C:\ 文件夹中的所有项目,然后按 Alt-Enter 以显示属性窗口。在这样做一次(使用您的代码)并获得 40 秒的经过时间之后,我现在从 两种方法 都经过了 20 秒。换句话说,你的代码实际上和 Windows 本身一样快,至少在我的机器上是这样。

【讨论】:

  • 感谢这个增强版,即使收益很小。似乎我们达到了限制,因此我们将在后台运行我们的进程并使用 Windows 支持的更改通知保持文件夹大小列表最新。与您建议的想法相同。
  • 此功能不会为大文件提供正确的文件夹大小。您必须同时使用高阶和低阶大小,用户 Lindley 在此解释:forums.codeguru.com/… 所以添加到 total_size 的行应该是 total_size += float( item[4] * ( 2 ** 32 ) + item[5] )
【解决方案6】:
# Size of File Folder/Directory in MBytes

import os

# pick a folder you have ...
folder = 'D:\\zz1'
folder_size = 0
for (path, dirs, files) in os.walk(folder):
  for file in files:
    filename = os.path.join(path, file)
    folder_size += os.path.getsize(filename)

print "Folder = %0.1f MB" % (folder_size/(1024*1024.0))

【讨论】:

  • 是什么让这个版本比问题中显示的程序更快?
猜你喜欢
  • 1970-01-01
  • 2011-08-17
  • 2015-07-09
  • 2016-05-19
  • 2011-01-12
  • 2017-08-13
相关资源
最近更新 更多