整理硬盘时最头疼的就是重复文件。本示例用 Directory.EnumerateFiles 递归列出全部文件,逐个计算 MD5 并按哈希分组,只要某组多于一个文件即为重复,便于后续人工确认删除。

实现思路

用字典以 MD5 为键、文件路径列表为值聚合;GetMd5 借助 MD5.ComputeHash 配合文件流读取,最后按分组打印。权限不足的目录会抛异常,遍历时用 try/catch 跳过即可。

完整代码

using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;

class Program
{
    static void Main(string[] args)
    {
        string root = args.Length > 0 ? args[0] : "D:/";
        var map = new Dictionary<string, List<string>>();   // md5 -> 路径列表

        foreach (string file in SafeEnumerate(root))
        {
            string md5 = GetMd5(file);
            if (!map.ContainsKey(md5)) map[md5] = new List<string>();
            map[md5].Add(file);
        }

        int total = 0;
        foreach (var pair in map)
        {
            if (pair.Value.Count < 2) continue;
            total += pair.Value.Count - 1;
            Console.WriteLine("MD5: " + pair.Key);
            foreach (string path in pair.Value)
                Console.WriteLine("  " + path);
        }
        Console.WriteLine("可清理重复文件数:" + total);
    }

    static IEnumerable<string> SafeEnumerate(string root)
    {
        foreach (string file in Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories))
        {
            try { yield return file; }
            catch (UnauthorizedAccessException) { /* 无权限跳过 */ }
            catch (IOException) { /* 被占用跳过 */ }
        }
    }

    static string GetMd5(string path)
    {
        using (var md5 = MD5.Create())
        using (var stream = File.OpenRead(path))
        {
            byte[] hash = md5.ComputeHash(stream);
            return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
        }
    }
}

使用建议

  • 先输出分组人工核对,确认无误再删除,避免误删同名不同内容文件;
  • 大文件较多时先比文件大小再比 MD5 可显著提速;
  • 跨盘符扫描建议输出清单后用资源管理器复核。

小结

MD5 分组是目前最可靠的重复文件判定手段之一,把同样的思路稍作改造,还能用于备份完整性校验与资源指纹统计。