python-3.x 如何有效地计算一个目录中的文件夹和文件大小?

zkure5ic  于 2023-02-14  发布在  Python
关注(0)|答案(1)|浏览(170)

如何有效地计算给定目录中每个子文件夹和文件的大小?
到目前为止我所拥有的代码做了我想要的事情,但由于我必须如何计算父文件夹大小,它效率低下,速度缓慢。
以下是我目前的时间安排:

Section 1: 0.53 s
Section 2: 30.71 s

代码:

import os
import time
import collections

def folder_size(directory):
    parents = []
    file_size = collections.defaultdict(int)
    parent_size = collections.defaultdict(int)

    t0 = time.time()

    #### Section 1 ####
    for root, dirs, files in os.walk(directory):
        root = os.path.abspath(root)
        parents.append(root)
        
        for f in files:
            f = os.path.join(root, f)
            file_size[f] += os.path.getsize(f)
    ###################

    t1 = time.time()
    print(f'walk time: {round(t1-t0, 2)}')   

    #### Section 2 ####
    for parent in parents:
        parent_split = parent.split(os.sep)
        for filename, value in file_size.items():
            parent_for_file = filename.split(os.sep)[:len(parent_split)]
            if parent_split == parent_for_file:
                parent_size[parent] += value
    ###################
    
    t2 = time.time()
    print(f'parent size time: {round(t2-t1, 2)}')   

    return file_size, parent_size

代码的第2部分效率低下,原因如下:

效率低下#1

我需要捕获没有文件的文件夹。例如,在这样的文件夹结构中:

TopFolder
├── FolderA
│   ├── folder_P1
│   │   ├── folder_P1__file_1.txt
│   │   └── folder_P1__file_2.txt
│   ├── folder_P10
│   │   ├── folder_P10__file_1.txt
│   │   └── folder_P10__file_2.txt
.
.
.

我希望最后得到每个目录的大小(以字节为单位),如下所示:

'..../TopFolder': 114000,
'..../TopFolder/FolderA': 38000,
'..../TopFolder/FolderA/folder_P1': 38,
'..../TopFolder/FolderA/folder_P10': 38,
.
.
.

为了获得包含子文件夹(如TopFolderFolderA)的文件夹的总大小,我将父文件夹分开存储,这样我就可以返回并根据文件大小计算它们的大小。

效率低下#2

这段代码非常慢,因为我必须对字符串进行split()运算来确定父文件夹(通过cProfile模块确认)。我必须这样做是因为如果我执行类似下面的代码片段的操作,某些文件夹的大小将无法正确计算。我也尝试过使用re.split(),但速度更慢。

#### Section 2 ####
    ...
    for parent in parents:
        for filename, value in file_size.items():
            if parent in filename:
                parent_size[parent] += value
    ...
###################

下面是if parent in filename的错误输出:

'..../TopFolder': 114000,
'..../TopFolder/FolderA': 38000,
'..../TopFolder/FolderA/folder_P1': 4256,
'..../TopFolder/FolderA/folder_P10': 456,
'..../TopFolder/FolderA/folder_P100': 76,
'..../TopFolder/FolderA/folder_P1000': 38,
.
.
.

下面是原始代码的正确输出:

'..../TopFolder': 114000,
'..../TopFolder/FolderA': 38000,
'..../TopFolder/FolderA/folder_P1': 38,
'..../TopFolder/FolderA/folder_P10': 38,
'..../TopFolder/FolderA/folder_P100': 38,
'..../TopFolder/FolderA/folder_P1000': 38,
.
.
.

第2节要么需要改进,使其运行得更快,要么需要将第2节合并到第1节中。我在互联网上搜索了一些想法,但只能找到有关计算顶级目录大小的信息,而且没有什么想法。
下面是我用来创建示例目录结构的代码:

import os

folder = 'TopFolder'
subfolders = ['FolderA', 'FolderB', 'FolderC']

for i in range(1000):
    for subfolder in subfolders:
        path = os.path.join(folder, subfolder, f'folder_P{i + 1}')
        if not os.path.isdir(path):
            os.makedirs(path)
        for k in range(2):
            with open(os.path.join(path, f'folder_P{i + 1}__file_{k + 1}.txt'), 'w') as file_out:
                file_out.write(f'Hello from file {k + 1}!\n')```
jjhzyzn0

jjhzyzn01#

使用os.walk时,您不必使用os.scandir生成的文件条目对象,os.walk会在内部调用这些对象。因此,您可以使用每个文件条目的stat对象,而不必为每个文件使用os.path.getsize进行单独的系统调用。您也不应该仅仅为了查找父目录名而解析路径,因为在列出具有该名称的目录时,您已经有了父目录名。
以下示例仅用0.2秒就可以为www.example.com上的测试目录结构生成所需的输出repl.it:

import os

def folder_size(directory):
    def _folder_size(directory):
        total = 0
        for entry in os.scandir(directory):
            if entry.is_dir():
                _folder_size(entry.path)
                total += parent_size[entry.path]
            else:
                size = entry.stat().st_size
                total += size
                file_size[entry.path] = size
        parent_size[directory] = total

    file_size = {}
    parent_size = {}
    _folder_size(directory)
    return file_size, parent_size

file_size, parent_size = folder_size('TopFolder')

演示:https://replit.com/@blhsing/SparseStainedNature

相关问题