《.NET 下最快比较两个文件内容是否相同》之我的看法验证
我对文件对比这一块还是比较感兴趣的,也想知道哪种方式性价比最高,效率最好,所以,根据这篇文章,我自己也自测一下,顺便留出自己对比的结果,供大佬们参考一二。大致对比方案我这边根据文章里的主要三个方案1.MD52.缓存长度读取比较3.缓存长度读取(Span)比较4.Hash2565.CRC(最后补的)新增了Hash256模式,原因是因为GitHub就是用Hash256来确定文件的唯一性的,所...
我对文件对比这一块还是比较感兴趣的,也想知道哪种方式性价比最高,效率最好,所以,根据这篇文章,我自己也自测一下,顺便留出自己对比的结果,供大佬们参考一二。
大致对比方案
我这边根据文章里的主要三个方案
1. MD5
2. 缓存长度读取比较
3. 缓存长度读取(Span)比较
4. Hash256
5. CRC(最后补的)
新增了Hash256模式,原因是因为GitHub就是用Hash256来确定文件的唯一性的,所以,也想测试下它的性能到底如何。
又新增了CRC模式,后来才想起来的。
代码大致如下
挺简单的一个抽象工具类,方便增加相关的相同测试。我也搜了OpenAi的建议和NewBing的建议,大致都是一样的建议。
![null 9161a77572ddd372c785f7e6315d7363.png](https://i-blog.csdnimg.cn/blog_migrate/ee20c96b091d9e2a6dc34656420218b0.png)
根据建议和参考文章
抽象工具
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
![null 9dd753f12bd152c89712b7fd67779c85.png](https://i-blog.csdnimg.cn/blog_migrate/f4b73a7d03d57c20389c0e5860e5e185.png)
测试2
![null f447ddf7471b7193790d06d4ec604eee.png](https://i-blog.csdnimg.cn/blog_migrate/5cf57d8c1676169ad7668be7d5a40a01.png)
测试3
![null 6d68fb681a2f2d4b0630d8315f526057.png](https://i-blog.csdnimg.cn/blog_migrate/7a14d3d5dd27d4af7c85237de7d97c13.png)
CRC补充三次
第一次
![null 0fc1058e42b1aa1466089f035571ad72.png](https://i-blog.csdnimg.cn/blog_migrate/8cbb313b320285f1888ea06382556b03.png)
第二次
![null 3f8df1e34954decf0eb77a4fefaf7d33.png](https://i-blog.csdnimg.cn/blog_migrate/391d99c40fdff04194a529b4cbd14efb.png)
第三次
![null db37aac130472eda225fd6314c53fd2d.png](https://i-blog.csdnimg.cn/blog_migrate/630c8658dc08dd6805092eeba418f2c9.png)
结果对比
![null 750588490e01efc31ccca2a30cd22642.png](https://i-blog.csdnimg.cn/blog_migrate/e39f5ff2934858eea639968c2186f25b.png)
根据我分析的表大致可以看出来,五种方式,根据各种不同的文件对比方式,在文件大小不一样的情况下,差别还是挺大的,特别是在大文件的情况下。
文件越大,反而MD5和Hash效果会更好。文件小于1G ?,FileSize_Span方式会更优,除了MD5,其他方式基本都差不多,CRC属于不上也不下。
虽然,环境的因素会有一定的影响,假设我这个就是客户的机器,那可能性也是存在的。
所以,在同一台机器上的多次测试也有存在的合理性。
那么,如果对性能很在意的话,可以根据文件大小与各种对比方法之间的关系,找到一个函数映射关系,从而找到一个最优的对比方法出来。
总结
还挺意外的,本来想验证这个Span方式为何快,却看到了另外问题点,出乎意料。
另外,也看到了Github采用Hash256在获取指纹上,速度还是相对稳定的。那么,用它来对比文件还是提取指纹应该是最佳之选(性能要求苛刻的,得自己搞模型求最佳函数映射关系)
还有其他的细节优化还没有验证,但是Span的方式是值得借鉴和学习的。
《.NET 下最快比较两个文件内容是否相同》参考地址
https://www.cnblogs.com/waku/p/11069214.html
代码地址
https://github.com/kesshei/FileCompare.git
https://gitee.com/kesshei/FileCompare.git
阅
一键三连呦!,感谢大佬的支持,您的支持就是我的动力!
![Logo](https://devpress.csdnimg.cn/79de2bf0b7994defa4242ef90d5513fa.jpg)
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)