实用技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > ASP.NET > 实用技巧 > .net哈希值筛选重复文件

.NET根据文件的哈希值筛选重复文件的实现思路

作者:醉酒的李白、

文章介绍了文件哈希值的概念,包括哈希算法、唯一性、固定长度、不可逆性以及其在文件验证、数据完整性检查、文件去重、密码存储等领域的应用,接着,通过一个示例代码说明了如何通过文件大小分组并比对哈希值来筛选出重复文件,感兴趣的朋友一起看看吧

如题。先介绍下概念。

Q1. 文件的哈希值是什么?

文件的哈希值是通过特定的哈希算法对文件内容进行计算后得到的固定长度的字符串(通常由数字和字母组成),它具有以下特点和相关信息:

哈希值在文件验证、数据完整性检查、文件去重、密码存储等众多领域都有广泛应用,是确保数据一致性和安全性的重要手段之一。

Q2. 比如一个文件 有1G大小,那么它的哈希值会多大?

文件哈希值的大小与文件本身的大小无关,而是由所使用的哈希算法决定。

常见的哈希算法及其生成的哈希值大小如下:

所以,一个 1GB 大小的文件,其哈希值的大小取决于选择的哈希算法,而不是文件自身的体积大小。

需求:客户有200+份文件来源于不同的三方平台,但是客户不确定是否有重复文件。可能来源不同但是文件相同。客户希望筛出这些重复文件。供用户搜索用,这样用户就只会搜出唯一的文件结果,而不是来源于不同渠道的相同文件。

实现思路:

先获取所有文件的大小,并按照大小group by,这样相同大小的文件就会在一组。文件大小不同一定不属于相同文件,文件大小相同有可能属于相同文件,也有可能是不同的文件刚好大小相等。按照这方式,遍历所有相同大小的组。比对哈希值。文件大小不同的就不用比较了。肯定不是相同文件。

/*
代码说明
FindDuplicateFilesGrouped 方法:
首先遍历传入的文件路径数组,将每个文件的信息(Id、FileName、FileSize)封装到 FileInfoEntity 对象中,并添加到 fileInfos 列表。
接着按文件大小对 fileInfos 进行分组,得到 sizeGroups。
针对每个大小组,计算组内每个文件的哈希值,把具有相同哈希值的文件存到 hashGroups 字典里。
最后,将 hashGroups 中包含多个文件的组添加到 duplicateFileGroups 列表,该列表即为最终按组返回的重复文件结果。
通过这种方式,就能方便地找出所有重复文件,并将它们按组分类返回。
*/

FileComparisonTool.cs

//FileComparisonTool.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
namespace ConsoleApp1_CompareFiles
{
    // 文件实体类
    public class FileInfoEntity
    {
        public Guid Id { get; set; }
        public MemoryStream Stream { get; set; }
        public string FileName { get; set; }
        public long FileSize { get; set; }
    }
    // 文件比较工具类
    public class FileComparisonTool
    {
        public static string[] GetAllFilesInFolder(string folderPath)
        {
            try
            {
                // 获取当前文件夹下的所有文件
                string[] files = Directory.GetFiles(folderPath);
                // 获取当前文件夹下的所有子文件夹
                string[] subFolders = Directory.GetDirectories(folderPath);
                foreach (string subFolder in subFolders)
                {
                    // 递归调用获取子文件夹下的所有文件
                    string[] subFiles = GetAllFilesInFolder(subFolder);
                    files = files.Concat(subFiles).ToArray();
                }
                return files;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"发生错误: {ex.Message}");
                return new string[0];
            }
        }
        // 计算文件哈希值
        private static string CalculateHash(Stream stream)
        {
            using (SHA256 sha256 = SHA256.Create())
            {
                byte[] hashBytes = sha256.ComputeHash(stream);
                return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
            }
        }
        // 入参是两个文件流,比较两个文件
        public static bool CompareFiles(Stream stream1, Stream stream2)
        {
            stream1.Position = 0;
            stream2.Position = 0;
            string hash1 = CalculateHash(stream1);
            string hash2 = CalculateHash(stream2);
            return hash1 == hash2;
        }
        // 入参是两个文件路径,比较两个文件
        public static bool CompareFiles(string filePath1, string filePath2)
        {
            using (FileStream stream1 = File.OpenRead(filePath1))
            using (FileStream stream2 = File.OpenRead(filePath2))
            {
                return CompareFiles(stream1, stream2);
            }
        }
        // 入参是文件路径的数组,找出重复文件
        public static List<FileInfoEntity> FindDuplicateFiles(string[] filePaths)
        {
            var fileInfos = new List<FileInfoEntity>();
            foreach (var filePath in filePaths)
            {
                var fileInfo = new FileInfo(filePath);
                fileInfos.Add(new FileInfoEntity
                {
                    Id = Guid.NewGuid(),
                    FileName = filePath,
                    FileSize = fileInfo.Length
                });
            }
            var groups = fileInfos.GroupBy(f => f.FileSize);
            var duplicateFiles = new List<FileInfoEntity>();
            foreach (var group in groups)
            {
                if (group.Count() > 1)
                {
                    var hashes = new Dictionary<string, FileInfoEntity>();
                    foreach (var file in group)
                    {
                        using (FileStream stream = File.OpenRead(file.FileName))
                        {
                            string hash = CalculateHash(stream);
                            if (hashes.ContainsKey(hash))
                            {
                                if (!duplicateFiles.Contains(hashes[hash]))
                                {
                                    duplicateFiles.Add(hashes[hash]);
                                }
                                duplicateFiles.Add(file);
                            }
                            else
                            {
                                hashes[hash] = file;
                            }
                        }
                    }
                }
            }
            return duplicateFiles;
        }
        /*
        代码说明
        FindDuplicateFilesGrouped 方法:
        首先遍历传入的文件路径数组,将每个文件的信息(Id、FileName、FileSize)封装到 FileInfoEntity 对象中,并添加到 fileInfos 列表。
        接着按文件大小对 fileInfos 进行分组,得到 sizeGroups。
        针对每个大小组,计算组内每个文件的哈希值,把具有相同哈希值的文件存到 hashGroups 字典里。
        最后,将 hashGroups 中包含多个文件的组添加到 duplicateFileGroups 列表,该列表即为最终按组返回的重复文件结果。
        通过这种方式,就能方便地找出所有重复文件,并将它们按组分类返回。
        */
        // 入参是文件路径数组,按组返回重复文件
        public static List<List<FileInfoEntity>> FindDuplicateFilesGrouped(string[] filePaths)
        {
            var fileInfos = new List<FileInfoEntity>();
            foreach (var filePath in filePaths)
            {
                var fileInfo = new FileInfo(filePath);
                fileInfos.Add(new FileInfoEntity
                {
                    Id = Guid.NewGuid(),
                    FileName = filePath,
                    FileSize = fileInfo.Length
                });
            }
            var sizeGroups = fileInfos.GroupBy(f => f.FileSize);
            var duplicateFileGroups = new List<List<FileInfoEntity>>();
            foreach (var sizeGroup in sizeGroups)
            {
                if (sizeGroup.Count() > 1)
                {
                    var hashGroups = new Dictionary<string, List<FileInfoEntity>>();
                    foreach (var file in sizeGroup)
                    {
                        using (FileStream stream = File.OpenRead(file.FileName))
                        {
                            string hash = CalculateHash(stream);
                            if (!hashGroups.ContainsKey(hash))
                            {
                                hashGroups[hash] = new List<FileInfoEntity>();
                            }
                            hashGroups[hash].Add(file);
                        }
                    }
                    foreach (var hashGroup in hashGroups.Values)
                    {
                        if (hashGroup.Count > 1)
                        {
                            duplicateFileGroups.Add(hashGroup);
                        }
                    }
                }
            }
            return duplicateFileGroups;
        }
    }
}
 

//

// Program.cs
// See https://aka.ms/new-console-template for more information
using ConsoleApp1_CompareFiles;
Console.WriteLine("Hello, World!");
string[] filePaths = { 
    "C:\\Users\\Aa\\Desktop\\新建文件夹\\1.txt",
    "C:\\Users\\Aa\\Desktop\\新建文件夹\\2.txt",
    "C:\\Users\\Aa\\Desktop\\新建文件夹\\3.txt",
    "C:\\Users\\Aa\\Desktop\\新建文件夹\\4.xlsx",
    "C:\\Users\\Aa\\Desktop\\新建文件夹\\5.xlsx",
    "C:\\Users\\Aa\\Desktop\\新建文件夹\\6.docx",
    "C:\\Users\\Aa\\Desktop\\新建文件夹\\7.docx",
    "C:\\Users\\Aa\\Desktop\\新建文件夹\\8.jpeg",
    "C:\\Users\\Aa\\Desktop\\新建文件夹\\9.jpeg",
    "C:\\Users\\Aa\\Desktop\\新建文件夹\\捕获 - 副本.PNG",
    "C:\\Users\\Aa\\Desktop\\新建文件夹\\捕获.PNG",
    "C:\\Users\\Aa\\Desktop\\新建文件夹\\微信图片_20250221165428.png",
};
string folderPath = @"C:\Users\Aa\Desktop\新建文件夹";
string[] allFiles = FileComparisonTool.GetAllFilesInFolder(folderPath);
var duplicateFileGroups = FileComparisonTool.FindDuplicateFilesGrouped(allFiles);
Console.WriteLine($"找到的重复文件组数量: {duplicateFileGroups.Count}");
foreach (var group in duplicateFileGroups)
{
    Console.WriteLine($"该组重复文件数量: {group.Count}");
    foreach (var file in group)
    {
        Console.WriteLine($"  文件 ID: {file.Id}, 文件名: {file.FileName}, 文件大小: {file.FileSize}");
    }
}

实现效果:

测试文件.txt文本的举例子。可以看到黄框和红框内的都属于相同文件。红框和黄框内的虽然字节数是一样的,相同大小会group by到一组。但是hash值肯定是不同的。

到此这篇关于.NET下根据文件的哈希值筛选重复文件的文章就介绍到这了,更多相关.net哈希值筛选重复文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文