目标:遍历文件夹,计算文件夹下包含文件和文件夹的大小。将这些结果存入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 shutildef 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_ok
为True
,无论目标目录是否存在,os.makedirs
会执行,不会报错。 - 如果
exist_ok
为False
,并且目标目录已经存在,os.makedirs
会引发一个FileExistsError
异常。
exist_ok=True
允许函数在调用时多次执行,即使已经创建了目录结构,也不会引发错误。
Step2 遍历文件夹,计算文件夹大小
设计三个函数:
-
get_file_size(file_path)
函数:- 输入:文件路径
file_path
。 - 输出:返回该文件的大小(字节数),使用
os.path.getsize
函数获取。
- 输入:文件路径
-
format_size(size_bytes)
函数:- 输入:一个表示字节数的整数
size_bytes
。 - 输出:返回格式化后的字符串,该字符串包含适当的单位(B、KB、MB、GB)以及转换后的大小值。该函数通过迭代循环,将字节数转换为合适的单位。
- 输入:一个表示字节数的整数
-
get_directory_size(directory_path)
函数:- 输入:目录路径
directory_path
。 - 输出:返回该目录及其子目录中所有文件的总大小。
- 思路:
- 使用
os.walk
遍历目录,得到每个文件的路径。 - 对于每个文件,调用
get_file_size
函数获取其大小,并累计到总大小。 - 对于每个子目录,递归调用
get_directory_size
函数,将返回的子目录大小累加到总大小。 - 返回总大小。
- 使用
- 输入:目录路径
这样,通过这三个函数的协作,可以获取文件和目录的大小信息,并且通过 format_size
函数,可以将字节数格式化为易读的字符串。
import osdef 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:breaksize_bytes /= 1024.0return f"{size_bytes:.2f} {unit}"def get_directory_size(directory_path):"""递归计算目录大小,包括目录中所有文件和子目录的大小"""total_size = 0for 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_sizeif __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 优化
回顾上面的计算文件夹大小的程序,可能有些可以改进的方向
- 异常问题: 目前的代码缺少对一些异常情况的处理,例如无法访问的文件或目录。
- 性能问题: 对于非常大的目录结构,递归遍历可能会导致重复计算,可能影响计算文件大小效率。
- 符号链接问题: 目前的程序不处理符号链接,可能会导致计算错误或无限循环。
- 文件类型判断和过滤问题: 目前程序对文件和目录的处理方式一样,没有区分文件类型。
- 异常处理问题
因为是对文件进行处理,所以通过增加文件处理异常:
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_bytesexcept (PermissionError, FileNotFoundError) as e:print(f"访问文件出错: {e}")return 0except Exception as e:print(f"出现异常: {e}")return 0
对 get_directory_size 函数进行异常处理
def get_directory_size(directory_path):"""递归计算目录大小,包括目录中所有文件和子目录的大小"""total_size = 0try: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 0return total_size
- 性能问题
我想利用数据库存储已经处理过的文件和文件夹。最简单的方式,数据库就用python自带的数据库,边做便设置数据库格式。