【笔记】计算文件夹的大小

目标:遍历文件夹,计算文件夹下包含文件和文件夹的大小。将这些结果存入python自带的数据库。

用大模型帮我设计并实现。

Step1 创建一个测试用的目录结构

创建目录结构如下所示:

TestDirectory/
│
├── EmptyFolder/
│
├── SmallFiles/
│   ├── file1.txt (1 KB)
│   ├── file2.txt (2 KB)
│   └── file3.txt (3 KB)
│
├── LargeFiles/
│   ├── big_file1.bin (10 MB)
│   ├── big_file2.bin (20 MB)
│   └── big_file3.bin (30 MB)
│
└── NestedFolders/
    ├── Subfolder1/
    │   ├── file4.txt (4 KB)
    │   ├── file5.txt (5 KB)
    │   └── EmptySubfolder1/
    │
    ├── Subfolder2/
    │   ├── file6.txt (6 KB)
    │   └── EmptySubfolder2/
    │
    └── Subfolder3/
        ├── file7.txt (7 KB)
        └── EmptySubfolder3/
import os
import shutil

def create_test_directory(root_path):
    # 创建根目录
    os.makedirs(root_path, exist_ok=True)

    # 创建空目录
    empty_folder_path = os.path.join(root_path, 'EmptyFolder')
    os.makedirs(empty_folder_path, exist_ok=True)

    # 创建包含小文件的目录
    small_files_path = os.path.join(root_path, 'SmallFiles')
    os.makedirs(small_files_path, exist_ok=True)
    for i in range(1, 4):
        file_path = os.path.join(small_files_path, f'file{i}.txt')
        with open(file_path, 'w') as file:
            file.write('x' * (i * 1024))  # 生成大小为 i KB 的文件

    # 创建包含大文件的目录
    large_files_path = os.path.join(root_path, 'LargeFiles')
    os.makedirs(large_files_path, exist_ok=True)
    for i in range(1, 4):
        file_path = os.path.join(large_files_path, f'big_file{i}.bin')
        with open(file_path, 'wb') as file:
            file.write(os.urandom(i * 10 * 1024 * 1024))  # 生成大小为 i * 10 MB 的二进制文件

    # 创建多层次嵌套目录
    nested_folders_path = os.path.join(root_path, 'NestedFolders')
    os.makedirs(nested_folders_path, exist_ok=True)
    for i in range(1, 4):
        subfolder_path = os.path.join(nested_folders_path, f'Subfolder{i}')
        os.makedirs(subfolder_path, exist_ok=True)
        file_path = os.path.join(subfolder_path, f'file{i + 3}.txt')
        with open(file_path, 'w') as file:
            file.write('x' * ((i + 3) * 1024))
        empty_subfolder_path = os.path.join(subfolder_path, f'EmptySubfolder{i}')
        os.makedirs(empty_subfolder_path, exist_ok=True)

os.makedirs函数中,exist_ok参数用于指定是否在目录已经存在的情况下忽略错误。

  • 如果exist_okTrue,无论目标目录是否存在,os.makedirs会执行,不会报错。
  • 如果exist_okFalse,并且目标目录已经存在,os.makedirs会引发一个FileExistsError异常。

exist_ok=True 允许函数在调用时多次执行,即使已经创建了目录结构,也不会引发错误。

Step2 遍历文件夹,计算文件夹大小

设计三个函数:

  1. get_file_size(file_path) 函数:

    • 输入:文件路径 file_path
    • 输出:返回该文件的大小(字节数),使用 os.path.getsize 函数获取。
  2. format_size(size_bytes) 函数:

    • 输入:一个表示字节数的整数 size_bytes
    • 输出:返回格式化后的字符串,该字符串包含适当的单位(B、KB、MB、GB)以及转换后的大小值。该函数通过迭代循环,将字节数转换为合适的单位。
  3. get_directory_size(directory_path) 函数:

    • 输入:目录路径 directory_path
    • 输出:返回该目录及其子目录中所有文件的总大小。
    • 思路:
      • 使用 os.walk 遍历目录,得到每个文件的路径。
      • 对于每个文件,调用 get_file_size 函数获取其大小,并累计到总大小。
      • 对于每个子目录,递归调用 get_directory_size 函数,将返回的子目录大小累加到总大小。
      • 返回总大小。

这样,通过这三个函数的协作,可以获取文件和目录的大小信息,并且通过 format_size 函数,可以将字节数格式化为易读的字符串。

import os

def get_file_size(file_path):
    """
    计算文件大小(字节数)
    """
    return os.path.getsize(file_path)

def format_size(size_bytes):
    """
    将字节数格式化为人类可读的字符串
    """
    for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
        if size_bytes < 1024.0:
            break
        size_bytes /= 1024.0
    return f"{size_bytes:.2f} {unit}"

def get_directory_size(directory_path):
    """
    递归计算目录大小,包括目录中所有文件和子目录的大小
    """
    total_size = 0
    for dirpath, dirnames, filenames in os.walk(directory_path):
        # 计算文件大小
        for filename in filenames:
            file_path = os.path.join(dirpath, filename)
            total_size += get_file_size(file_path)

        # 计算子目录大小
        for dirname in dirnames:
            subdirectory_path = os.path.join(dirpath, dirname)
            total_size += get_directory_size(subdirectory_path)

    return total_size

上面返回的total_size是字节数。

另外说一下,函数中用到的 print(f"xxxx") 中的 f"xxxx" 是一个 f-string,是一种字符串格式化的方式,是在字符串前加上 f 或 F 前缀的字符串字面值。它允许在字符串中嵌入表达式,这些表达式会在运行时求值,并将结果插入到字符串中。可以使用大括号 {} 括起表达式,这些表达式将在运行时被替换为相应的值。例如:

name = "秦汉唐"
age = 25
print(f"姓名: {name} \t年龄 {age}")

f-string 中的 {name} 和 {age} 会分别被替换为变量 name 和 age 的值。

在测试的时候(testRun.py中):

import os
import shutil
from folder_size_calculator import get_directory_size, format_size, get_file_size

if __name__=="__main__":
    # 测试函数
    create_test_directory('TestDirectory')

    # 测试
    directory_path = "TestDirectory"  # 替换为你的目录路径
    total_size = get_directory_size(directory_path)
    formatted_size = format_size(total_size)
    print(f" '{directory_path}' 文件夹大小为: {formatted_size}")

执行testRun.py,结果类似:

>python testRun.py
 'TestDirectory' 文件夹大小为: 120.07 MB

Step3 优化

回顾上面的计算文件夹大小的程序,可能有些可以改进的方向

  • 异常问题: 目前的代码缺少对一些异常情况的处理,例如无法访问的文件或目录。
  • 性能问题: 对于非常大的目录结构,递归遍历可能会导致重复计算,可能影响计算文件大小效率。
  • 符号链接问题: 目前的程序不处理符号链接,可能会导致计算错误或无限循环。
  • 文件类型判断和过滤问题: 目前程序对文件和目录的处理方式一样,没有区分文件类型。
  1. 异常处理问题
    因为是对文件进行处理,所以通过增加文件处理异常:
try:
    # 尝试获取文件大小或目录列表
    # ...
except (PermissionError, OSError) as e:
    print(f"Error accessing file or directory: {e}")
except FileNotFoundError as e:
    print(f"File not found: {e}")
except Exception as e:
    print(f"An unexpected error occurred: {e}")

对 get_file_size 函数进行异常处理

def get_file_size(file_path):
    """
    计算文件大小(字节数)
    """
    try:
        size_bytes = os.path.getsize(file_path)
        return size_bytes
    except (PermissionError, FileNotFoundError) as e:
        print(f"访问文件出错: {e}")
        return 0
    except Exception as e:
    	print(f"出现异常: {e}")
    	return 0

对 get_directory_size 函数进行异常处理

def get_directory_size(directory_path):
    """
    递归计算目录大小,包括目录中所有文件和子目录的大小
    """
    total_size = 0

    try:
        for dirpath, dirnames, filenames in os.walk(directory_path):
            # 计算文件大小
            for filename in filenames:
                file_path = os.path.join(dirpath, filename)
                total_size += os.path.getsize(file_path)

            # 计算子目录大小
            for dirname in dirnames:
                subdirectory_path = os.path.join(dirpath, dirname)
                total_size += get_directory_size(subdirectory_path)

    except (PermissionError, FileNotFoundError) as e:
        print(f"访问文件异常: {e}")
        return 0  # 或者抛出其他异常
    except Exception as e:
    	print(f"出现异常: {e}")
    	return 0

    return total_size
  1. 性能问题
    我想利用数据库存储已经处理过的文件和文件夹。最简单的方式,数据库就用python自带的数据库,边做便设置数据库格式。

你可能感兴趣的:(笔记,数据库,服务器)