本地文件重复检测与清理工具:释放你的存储空间


背景介绍

在日常使用电脑的过程中,我们经常会遇到文件重复的问题。比如多次下载同一个文件、备份时复制了相同的照片或文档,这些重复文件会占用大量的存储空间。手动查找和清理这些重复文件不仅耗时,还容易遗漏或误删重要文件。因此,开发一个自动化的重复文件检测与清理工具就显得非常有必要。

思路分析

要实现一个高效的重复文件检测与清理工具,我们需要解决以下几个核心问题:

  1. 如何遍历目录:使用递归方式遍历指定目录下的所有文件
  2. 如何检测重复文件:通过计算文件内容的哈希值来判断文件是否相同
  3. 如何高效处理大文件:采用分块读取的方式计算哈希值,避免内存占用过高
  4. 如何与用户交互:让用户选择保留哪些文件,删除重复项
  5. 如何保证安全性:支持模拟删除(dry-run)模式,避免误删重要文件

代码实现

以下是完整的Python实现代码:

import os
import hashlib
import argparse

def calculate_hash(file_path, block_size=1024*1024):
    """
    计算文件的MD5哈希值,分块读取处理大文件
    :param file_path: 文件路径
    :param block_size: 分块大小,默认1MB
    :return: 文件的MD5哈希值字符串
    """
    hash_obj = hashlib.md5()
    try:
        with open(file_path, 'rb') as f:
            while chunk := f.read(block_size):
                hash_obj.update(chunk)
        return hash_obj.hexdigest()
    except Exception as e:
        print(f"Error calculating hash for {file_path}: {e}")
        return None

def find_duplicates(directory, min_size=0):
    """
    查找指定目录下的重复文件
    :param directory: 目标目录
    :param min_size: 最小文件大小(字节),小于该大小的文件将被忽略
    :return: 重复文件组列表,每个组是一个文件路径列表
    """
    print(f"开始扫描目录:{directory}")

    # 先按文件大小分组,减少哈希计算次数
    size_map = {}
    for root, _, files in os.walk(directory):
        for file in files:
            file_path = os.path.join(root, file)
            try:
                file_size = os.path.getsize(file_path)
                if file_size < min_size:
                    continue
                if file_size not in size_map:
                    size_map[file_size] = []
                size_map[file_size].append(file_path)
            except Exception as e:
                print(f"无法访问文件 {file_path}: {e}")
                continue

    # 对相同大小的文件计算哈希值,找出重复文件
    duplicate_groups = []
    for size, files in size_map.items():
        if len(files) < 2:
            continue

        hash_map = {}
        for file_path in files:
            file_hash = calculate_hash(file_path)
            if file_hash is None:
                continue
            if file_hash not in hash_map:
                hash_map[file_hash] = []
            hash_map[file_hash].append(file_path)

        # 将重复的文件组加入结果
        for paths in hash_map.values():
            if len(paths) > 1:
                duplicate_groups.append(paths)

    print(f"扫描完成:共发现 {len(duplicate_groups)} 组重复文件\n")
    return duplicate_groups

def print_duplicate_groups(duplicate_groups):
    """
    打印重复文件组信息
    :param duplicate_groups: 重复文件组列表
    """
    if not duplicate_groups:
        print("未发现重复文件")
        return

    for i, group in enumerate(duplicate_groups, 1):
        # 计算组内第一个文件的哈希值用于显示
        file_hash = calculate_hash(group[0])
        print(f"--- 重复组{i}(哈希:{file_hash[:16]}...)---")
        for j, path in enumerate(group, 1):
            print(f"{j}. {path}")
        print()

def interactive_delete(duplicate_groups, dry_run=False):
    """
    交互式删除重复文件
    :param duplicate_groups: 重复文件组列表
    :param dry_run: 是否模拟删除(不实际删除文件)
    """
    if not duplicate_groups:
        return

    # 确认是否删除
    confirm = input("是否删除重复文件?(y/n):").strip().lower()
    if confirm != 'y':
        print("取消删除操作")
        return

    total_freed = 0
    deleted_count = 0

    for group in duplicate_groups:
        print("\n--- 处理重复组 ---")
        for idx, path in enumerate(group, 1):
            print(f"{idx}. {path}")

        # 让用户选择保留的文件
        choice = input("请选择保留的文件序号(默认保留第1个):").strip()
        keep_idx = int(choice) - 1 if choice.isdigit() else 0

        # 确保选择的序号有效
        if keep_idx < 0 or keep_idx >= len(group):
            print("无效的序号,默认保留第1个文件")
            keep_idx = 0

        keep_path = group[keep_idx]
        print(f"保留文件:{keep_path}")

        # 删除其他重复文件
        for path in group:
            if path != keep_path:
                try:
                    file_size = os.path.getsize(path)
                    if dry_run:
                        print(f"Dry run: 将要删除 {path}(大小:{file_size}字节)")
                    else:
                        os.remove(path)
                        print(f"已删除:{path}")
                    total_freed += file_size
                    deleted_count += 1
                except Exception as e:
                    print(f"删除 {path} 失败:{e}")

    # 显示统计信息
    print(f"\n操作完成:共处理 {len(duplicate_groups)} 组重复文件")
    print(f"已删除 {deleted_count} 个文件")
    print(f"释放空间:{total_freed / (1024*1024):.2f} MB")

def main():
    # 解析命令行参数
    parser = argparse.ArgumentParser(description="本地文件重复检测与清理工具")
    parser.add_argument("--dir", required=True, help="目标目录路径")
    parser.add_argument("--min-size", type=int, default=0, help="最小文件大小(字节),默认0")
    parser.add_argument("--dry-run", action="store_true", help="模拟删除模式,不实际删除文件")
    args = parser.parse_args()

    # 检查目录是否存在
    if not os.path.isdir(args.dir):
        print(f"错误:目录 {args.dir} 不存在")
        return

    # 查找重复文件
    duplicate_groups = find_duplicates(args.dir, args.min_size)

    # 打印重复文件组
    print_duplicate_groups(duplicate_groups)

    # 交互式删除
    if duplicate_groups:
        interactive_delete(duplicate_groups, args.dry_run)

if __name__ == "__main__":
    main()

代码说明

  1. 哈希计算函数calculate_hash使用MD5算法计算文件哈希值,分块读取处理大文件,避免内存占用过高。

  2. 重复文件检测find_duplicates先按文件大小分组,只有大小相同的文件才可能是重复文件,这样可以减少哈希计算的次数,提高效率。

  3. 交互式删除interactive_delete让用户选择保留哪些文件,支持dry-run模式,确保操作安全。

  4. 命令行参数:使用argparse处理命令行参数,支持指定目录、最小文件大小和模拟删除模式。

使用示例

# 基本用法
python dup_cleaner.py --dir /path/to/your/directory

# 忽略小于1KB的文件
python dup_cleaner.py --dir /path/to/your/directory --min-size 1024

# 模拟删除(不实际删除文件)
python dup_cleaner.py --dir /path/to/your/directory --dry-run

总结

这个工具解决了重复文件检测与清理的核心问题,具有以下特点:

  1. 高效性:通过先按大小分组再计算哈希的方式,减少了不必要的计算
  2. 安全性:支持模拟删除模式,避免误删重要文件
  3. 易用性:交互式界面让用户可以灵活选择保留哪些文件
  4. 健壮性:包含错误处理,能够处理各种异常情况

该工具可以帮助用户快速清理电脑中的重复文件,释放存储空间,提高系统效率。同时,代码结构清晰,易于扩展,可以根据需要添加更多功能,比如支持不同的哈希算法、忽略特定目录、导出结果到文件等。
“`


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注