《.NET 下最快比较两个文件内容是否相同》之我的看法验证

news/2025/2/16 6:10:03/

我对文件对比这一块还是比较感兴趣的,也想知道哪种方式性价比最高,效率最好,所以,根据这篇文章,我自己也自测一下,顺便留出自己对比的结果,供大佬们参考一二。

大致对比方案

我这边根据文章里的主要三个方案

  1. MD5
  2. 缓存长度读取比较
  3. 缓存长度读取(Span)比较
  4. Hash256
  5. CRC(最后补的)

新增了Hash256模式,原因是因为GitHub就是用Hash256来确定文件的唯一性的,所以,也想测试下它的性能到底如何。

又新增了CRC模式,后来才想起来的。

代码大致如下

挺简单的一个抽象工具类,方便增加相关的相同测试。
我也搜了OpenAi的建议和NewBing的建议,大致都是一样的建议。

根据建议和参考文章

抽象工具

public abstract class AFileCompare
{public AFileCompare(string name){this.Name = name;}public string Name { get; set; }public bool Compare(string file1, string file2){var result = false;Stopwatch stopwatch = Stopwatch.StartNew();if (Check(file1, file2)){result = CompareCore(file1, file2);}stopwatch.Stop();TimeSpan = stopwatch.Elapsed;return result;}public abstract bool CompareCore(string file1, string file2);public TimeSpan TimeSpan { get; set; }public bool Check(string file1, string file2){if (file1 == file2){return true;}if (new FileInfo(file1).Length == new FileInfo(file2).Length){return true;}return false;}public override string ToString(){return $"{Name}耗时:{TimeSpan.TotalSeconds}秒";}
}

MD5工具

public class MD5Compare : AFileCompare
{public MD5Compare() : base("MD5 "){}public override bool CompareCore(string file1, string file2){using (var md5 = MD5.Create()){byte[] one, two;using (var fs1 = File.Open(file1, FileMode.Open)){// 以FileStream读取文件内容,计算HASH值one = md5.ComputeHash(fs1);}using (var fs2 = File.Open(file2, FileMode.Open)){// 以FileStream读取文件内容,计算HASH值two = md5.ComputeHash(fs2);}for (int i = 0; i < one.Length; i++){if (one[i] != two[i]){return false;}}return true;}}
}

Hash256

public class HashCompare : AFileCompare
{public HashCompare() : base("Hash256"){}public override bool CompareCore(string file1, string file2){byte[] one, two;using (SHA1 mySHA1 = SHA1.Create()){using (FileStream stream = File.OpenRead(file1)){one = mySHA1.ComputeHash(stream);}}using (SHA1 mySHA1 = SHA1.Create()){using (FileStream stream = File.OpenRead(file2)){two = mySHA1.ComputeHash(stream);}}for (int i = 0; i < one.Length; i++){if (one[i] != two[i]){return false;}}return true;}
}

缓存长度读取比较

public class FileSizeCompare : AFileCompare
{public FileSizeCompare() : base("FileSize_4096"){}public override bool CompareCore(string file1, string file2){using (FileStream fs1 = new FileStream(file1, FileMode.Open))using (FileStream fs2 = new FileStream(file2, FileMode.Open)){byte[] buffer1 = new byte[4096];byte[] buffer2 = new byte[4096];int bytesRead1;int bytesRead2;do{bytesRead1 = fs1.Read(buffer1, 0, buffer1.Length);bytesRead2 = fs2.Read(buffer2, 0, buffer2.Length);if (bytesRead1 != bytesRead2){return false;}for (int i = 0; i < bytesRead1; i++){if (buffer1[i] != buffer2[i]){return false;}}} while (bytesRead1 > 0);return true;}}
}

缓存长度读取(Span)比较

public class FileSizeCompare2 : AFileCompare
{public FileSizeCompare2() : base("FileSize_4096_Span"){}public override bool CompareCore(string file1, string file2){using (FileStream fs1 = new FileStream(file1, FileMode.Open))using (FileStream fs2 = new FileStream(file2, FileMode.Open)){byte[] buffer1 = new byte[4096];byte[] buffer2 = new byte[4096];int bytesRead1;int bytesRead2;do{bytesRead1 = fs1.Read(buffer1, 0, buffer1.Length);bytesRead2 = fs2.Read(buffer2, 0, buffer2.Length);if (bytesRead1 != bytesRead2){return false;}if (!((ReadOnlySpan<byte>)buffer1).SequenceEqual((ReadOnlySpan<byte>)buffer2)){return false;}} while (bytesRead1 > 0);return true;}}
}

CRC(补充)

public class CRCCompare : AFileCompare
{public CRCCompare() : base("CRC"){}public override bool CompareCore(string file1, string file2){Crc32Algorithm crc32 = new Crc32Algorithm();byte[] one, two;using (var fs1 = File.Open(file1, FileMode.Open)){one = crc32.ComputeHash(fs1);}using (var fs2 = File.Open(file2, FileMode.Open)){two = crc32.ComputeHash(fs2);}for (int i = 0; i < one.Length; i++){if (one[i] != two[i]){return false;}}return true;}
}

main 入口

static void Main(string[] args)
{var files = new List<(string name, string source, string target)>(){("ubuntu_4.58 GB",@"E:\重装系统\ubuntu-22.04.2-desktop-amd64.iso",@"E:\重装系统\ubuntu-22.04.2-desktop-amd64 - 副本.iso"),("Docker_523 MB",@"E:\重装系统\Docker Desktop Installer(1).exe",@"E:\重装系统\Docker Desktop Installer(1) - 副本.exe"),("Postman_116 MB",@"E:\重装系统\Postman-win64-8.5.0-Setup.exe",@"E:\重装系统\Postman-win64-8.5.0-Setup - 副本.exe")};var list = new List<AFileCompare>();list.Add(new MD5Compare());list.Add(new HashCompare());list.Add(new FileSizeCompare());list.Add(new FileSizeCompare2());foreach ((string name, string source, string target) in files){foreach (var item in list){var result = item.Compare(source, target);Console.WriteLine($"{name} - {item.Name} 结果:{result} {item}");}Console.WriteLine();}Console.WriteLine("测试完毕");Console.ReadLine();
}

测试结果如下

主要是对3个文件,4.5G,500M,100M文件进行了测试,大概也能看出来点结果了。

测试1

测试2

测试3

CRC补充三次

第一次

第二次

第三次

结果对比

根据我分析的表大致可以看出来,五种方式,根据各种不同的文件对比方式,在文件大小不一样的情况下,差别还是挺大的,特别是在大文件的情况下。

文件越大,反而MD5和Hash效果会更好。
文件小于1G ?,FileSize_Span方式会更优,除了MD5,其他方式基本都差不多,CRC属于不上也不下。

虽然,环境的因素会有一定的影响,假设我这个就是客户的机器,那可能性也是存在的。

所以,在同一台机器上的多次测试也有存在的合理性。

那么,如果对性能很在意的话,可以根据文件大小与各种对比方法之间的关系,找到一个函数映射关系,从而找到一个最优的对比方法出来。

总结

还挺意外的,本来想验证这个Span方式为何快,却看到了另外问题点,出乎意料。

另外,也看到了Github采用Hash256在获取指纹上,速度还是相对稳定的。那么,用它来对比文件还是提取指纹应该是最佳之选(性能要求苛刻的,得自己搞模型求最佳函数映射关系)

还有其他的细节优化还没有验证,但是Span的方式是值得借鉴和学习的。

代码地址

https://github.com/kesshei/FileCompare.git

https://gitee.com/kesshei/FileCompare.git

一键三连呦!,感谢大佬的支持,您的支持就是我的动力!


http://www.ppmy.cn/news/252291.html

相关文章

Linux驱动开发(使用I2C总线设备驱动模型编写AT24C02驱动程序)

文章目录 前言一、I2C总线设备驱动模型二、设备树编写三、驱动程序编写1.提供i2c_driver结构体变量并且注册2.注册file_operations结构体3.操作AT24C02 四、应用程序编写五、上机测试总结 前言 本篇文章将讲解如何使用I2C总线设备驱动模型编写AT24C02驱动程序。 一、I2C总线设…

PyGame游戏编程

Python非常受欢迎的一个原因是它的应用领域非常广泛&#xff0c;其中就包括游戏开发。而是用Python进行游戏开发的首选模块就是PyGame。 1. 初识Pygame PyGame是跨平台Python模块&#xff0c;专为电子游戏设计&#xff0c;包含图像、声音等&#xff0c;创建在SDL&#xff08;…

软件过程与管理——民宿管理系统的项目实践报告(文档+ppt+图表源文件)

目录 一、题目分析与设计 二、评分标准 三、文档目录 四、文档下载 一、题目分析与设计 1、团队组织建设 同学们以3-5人为一组&#xff0c;最多5人一组&#xff0c;每组选择一个具体的软件项目&#xff0c;如现进行的个人毕业设计题目等为主题&#xff0c;要求项目的工作…

【25JavaScript 调试】JavaScript调试技巧:掌握console.log、断点调试和浏览器开发者工具,轻松解决代码中的错误和异常情况

JavaScript 调试 调试是在开发过程中解决问题和排除错误的关键技能。在 JavaScript 中&#xff0c;我们可以使用多种工具和技术来帮助我们进行调试&#xff0c;以快速定位和修复代码中的问题。 使用 console.log() 输出调试信息 console.log() 是调试中最常用的方法之一。它…

DALL·E 2:艺术性的人工智能

DALLE 2 是 OpenAI 公司最新推出的人工智能工具&#xff0c;它具有创造性和艺术性。DALLE 2 可以生成令人惊奇的图像&#xff0c;并且它可以理解文本&#xff0c;根据文本生成图像。 DALLE 2 使用了深度学习技术&#xff0c;可以识别复杂的图像结构和图形。它可以根据文本描述生…

DALL-E如何使用

DALL-E是由OpenAI开发的一个人工智能模型,可以根据文字描述生成图像。它是基于深度学习的模型,在运行时需要大量的计算资源。 使用OpenAI API使用DALL-E的示例代码如下: import requests from requests.structures import CaseInsensitiveDictimport jsonQUERY_URL = &quo…

刷爆 AI 圈!基于 Transformer 的 DALL-E 代码刚刚开源了

点击上方“视学算法”&#xff0c;选择加"星标"或“置顶” 重磅干货&#xff0c;第一时间送达 转自 | AI科技评论 OpenAI在1月5日公布DALL-E模型以来&#xff0c;人们都惊艳于模型的语言想象力是如此丰富和细致。如今&#xff0c;我们终于等到了论文的公布&#xff0…

AI-多模态-文本->图像-2021:DALL-E模型【OpenAI】

Dall-e&#xff1a;从拟物文字到图片的创造 人类不断地从五种感官接收和整合信息&#xff0c;通过视觉、听觉、触觉、嗅觉和味觉等生物信息来理解文字和图片。然而文字和图片属于符号&#xff0c;Dall-e模型在理解符号的含义时并不能通过生物信息的传递。通过将对自然语言的理…