中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在.NET CORE中比較兩個文件內容是否相同

發布時間:2021-06-08 16:24:31 來源:億速云 閱讀:123 作者:Leah 欄目:開發技術

怎么在.NET CORE中比較兩個文件內容是否相同?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

/// <summary>
/// MD5
/// </summary>
/// <param name="file1"></param>
/// <param name="file2"></param>
/// <returns></returns>
private static bool CompareByMD5(string file1, string file2)
{
 // 使用.NET內置的MD5庫
 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);
 }
 // 將MD5結果(字節數組)轉換成字符串進行比較
 return BitConverter.ToString(one) == BitConverter.ToString(two);
 }
}

比較結果:

Method: CompareByMD5, Identical: True. Elapsed: 00:00:05.7933178

耗時5.79秒,感覺還不錯.然而,這是最佳的解決方案嗎?

其實我們仔細想一下,答案應該是否定的.

因為任何哈希算法本質上都是對字節進行一定的計算,而計算過程是要消耗時間的.

很多下載網站上提供了下載文件的哈希值,那是因為下載的源文件本身不會改變,只需要計算一次源文件的哈希值,提供給用戶驗證即可.

而我們的需求中,兩個文件都是不固定的,那么每次都要計算兩個文件的哈希值,就不太合適了.

所以,哈希比較這個方案被PASS.

這種求算法最優解的問題,我以往的經驗是: 去stackoverflow查找 :)

經過我的艱苦努力,找到了一個非常切題的答案: How to compare 2 files fast using .NET?

得贊最多一個答案,將代碼改造了一下放入工程中:

/// <summary>
/// https://stackoverflow.com/a/1359947
/// </summary>
/// <param name="file1"></param>
/// <param name="file2"></param>
/// <returns></returns>
private static bool CompareByToInt64(string file1, string file2)
{
 const int BYTES_TO_READ = sizeof(Int64); // 每次讀取8個字節
 int iterations = (int)Math.Ceiling((double)new FileInfo(file1).Length / BYTES_TO_READ); // 計算讀取次數

 using (FileStream fs1 = File.Open(file1, FileMode.Open))
 using (FileStream fs2 = File.Open(file2, FileMode.Open))
 {
 byte[] one = new byte[BYTES_TO_READ];
 byte[] two = new byte[BYTES_TO_READ];

 for (int i = 0; i < iterations; i++)
 {
  // 循環讀取到字節數組中
  fs1.Read(one, 0, BYTES_TO_READ);
  fs2.Read(two, 0, BYTES_TO_READ);

  // 轉換為Int64進行數值比較
  if (BitConverter.ToInt64(one, 0) != BitConverter.ToInt64(two, 0))
  return false;
 }
 }

 return true;
}

該方法基本的原理是循環讀取兩個文件,每次讀取8個字節,轉換為Int64,再進行數值比較.那么效率如何呢?

Method: CompareByToInt64, Identical: True. Elapsed: 00:00:08.0918099

什么?8秒!竟然比MD5還慢?這不是SO得贊最多的答案嗎,怎么會這樣?

其實分析一下不難想到原因,因為每次只讀取8個字節,程序頻繁的進行IO操作,導致性能低下.看來SO上的答案也不能迷信啊!

那么優化的方向就變為了如何減少IO操作帶來的損耗.

既然每次8個字節太少了,我們定義一個大一些的字節數組,比如1024個字節.每次讀取1024個字節到數組中,然后進行字節數組的比較.

但是這樣又帶來一個新問題,就是如何快速比較兩個字節數組是否相同?

我首先想到的是在MD5方法中用過的----將字節數組轉換成字符串進行比較:

/// <summary>
/// 讀入到字節數組中比較(轉為String比較)
/// </summary>
/// <param name="file1"></param>
/// <param name="file2"></param>
/// <returns></returns>
private static bool CompareByString(string file1, string file2)
{
 const int BYTES_TO_READ = 1024 * 10;

 using (FileStream fs1 = File.Open(file1, FileMode.Open))
 using (FileStream fs2 = File.Open(file2, FileMode.Open))
 {
 byte[] one = new byte[BYTES_TO_READ];
 byte[] two = new byte[BYTES_TO_READ];
 while (true)
 {
  int len1 = fs1.Read(one, 0, BYTES_TO_READ);
  int len2 = fs2.Read(two, 0, BYTES_TO_READ);
  if (BitConverter.ToString(one) != BitConverter.ToString(two)) return false;
  if (len1 == 0 || len2 == 0) break; // 兩個文件都讀取到了末尾,退出while循環
 }
 }

 return true;
}

結果:

Method: CompareByString, Identical: True. Elapsed: 00:00:07.8088732

耗時也接近8秒,比上一個方法強不了多少.

分析一下原因,在每次循環中,字符串的轉換是一個非常耗時的操作.那么有沒有不進行類型轉換的字節數組比較方法呢?

我想到了LINQ中有一個比較序列的方法SequenceEqual,我們嘗試使用該方法比較:

/// <summary>
/// 讀入到字節數組中比較(使用LINQ的SequenceEqual比較)
/// </summary>
/// <param name="file1"></param>
/// <param name="file2"></param>
/// <returns></returns>
private static bool CompareBySequenceEqual(string file1, string file2)
{
 const int BYTES_TO_READ = 1024 * 10;

 using (FileStream fs1 = File.Open(file1, FileMode.Open))
 using (FileStream fs2 = File.Open(file2, FileMode.Open))
 {
 byte[] one = new byte[BYTES_TO_READ];
 byte[] two = new byte[BYTES_TO_READ];
 while (true)
 {
  int len1 = fs1.Read(one, 0, BYTES_TO_READ);
  int len2 = fs2.Read(two, 0, BYTES_TO_READ);
  if (!one.SequenceEqual(two)) return false;
  if (len1 == 0 || len2 == 0) break; // 兩個文件都讀取到了末尾,退出while循環
 }
 }

 return true;
}

結果:

Method: CompareBySequenceEqual, Identical: True. Elapsed: 00:00:08.2174360

竟然比前兩個都要慢(實際這也是所有方案中最慢的一個),LINQ的SequenceEqual看來不是為了效率而生.

那么我們不用那些花哨的功能,回歸質樸,老實兒的使用while循環比較字節數組怎么樣呢?

/// <summary>
/// 讀入到字節數組中比較(while循環比較字節數組)
/// </summary>
/// <param name="file1"></param>
/// <param name="file2"></param>
/// <returns></returns>
private static bool CompareByByteArry(string file1, string file2)
{
 const int BYTES_TO_READ = 1024 * 10;

 using (FileStream fs1 = File.Open(file1, FileMode.Open))
 using (FileStream fs2 = File.Open(file2, FileMode.Open))
 {
 byte[] one = new byte[BYTES_TO_READ];
 byte[] two = new byte[BYTES_TO_READ];
 while (true)
 {
  int len1 = fs1.Read(one, 0, BYTES_TO_READ);
  int len2 = fs2.Read(two, 0, BYTES_TO_READ);
  int index = 0;
  while (index < len1 && index < len2)
  {
  if (one[index] != two[index]) return false;
  index++;
  }
  if (len1 == 0 || len2 == 0) break;
 }
 }

 return true;
}

結果是....

Method: CompareByByteArry, Identical: True. Elapsed: 00:00:01.5356821

1.53秒!大突破!看來有時候看起來笨拙的方法反而效果更好!

試驗到此,比較兩個900多MB的文件耗時1.5秒左右,讀者對于該方法是否滿意呢?

No!我不滿意!我相信通過努力,一定會找到更快的方法的!

同樣.NET CORE也在為了編寫高性能代碼而不斷的優化中.

那么,我們如何繼續優化我們的代碼呢?

我突然想到在C# 7.2中加入的一個新的值類型: Span<T>,它用來代表一段連續的內存區域,并提供一系列可操作該區域的方法.

對于我們的需求,因為我們不會更改數組的值,所以可以使用另外一個只讀的類型ReadOnlySpan<T>追求更高的效率.

修改代碼,使用ReadOnlySpan<T>:

/// <summary>
/// 讀入到字節數組中比較(ReadOnlySpan)
/// </summary>
/// <param name="file1"></param>
/// <param name="file2"></param>
/// <returns></returns>
private static bool CompareByReadOnlySpan(string file1, string file2)
{
 const int BYTES_TO_READ = 1024 * 10;

 using (FileStream fs1 = File.Open(file1, FileMode.Open))
 using (FileStream fs2 = File.Open(file2, FileMode.Open))
 {
 byte[] one = new byte[BYTES_TO_READ];
 byte[] two = new byte[BYTES_TO_READ];
 while (true)
 {
  int len1 = fs1.Read(one, 0, BYTES_TO_READ);
  int len2 = fs2.Read(two, 0, BYTES_TO_READ);
  // 字節數組可直接轉換為ReadOnlySpan
  if (!((ReadOnlySpan<byte>)one).SequenceEqual((ReadOnlySpan<byte>)two)) return false;
  if (len1 == 0 || len2 == 0) break; // 兩個文件都讀取到了末尾,退出while循環
 }
 }

 return true;
}

核心是用來比較的SequenceEqual方法,該方法是ReadOnlySpan的一個擴展方法,要注意它只是方法名與LINQ中一樣,實現完全不同.

那么該方法的表現如何呢?

Method: CompareByReadOnlySpan, Identical: True. Elapsed: 00:00:00.9287703

不 到 一 秒!

相對上一個已經不錯的結果,速度提高了差不多40%!

對此結果,我個人覺得已經很滿意了,如果各位有更快的方法,請不吝賜教,我非常歡迎!

關于Span<T>結構類型,各位讀者如有興趣,可瀏覽該文章,該文有非常詳細的介紹.

后記

文中的代碼只是出于實驗性質,實際應用中仍可以繼續細節上的優化, 如:

  • 如兩個文件大小不同,直接返回false

  • 如果兩個文件路徑相同,直接返回true

  • ...

試驗工程的Main方法源碼:

static void Main(string[] args)
{
 string file1 = @"C:\Users\WAKU\Desktop\file1.ISO";
 string file2 = @"C:\Users\WAKU\Desktop\file2.ISO";
 var methods = new Func<string, string, bool>[] { CompareByMD5, CompareByToInt64, CompareByByteArry, CompareByReadOnlySpan };
 foreach (var method in methods)
 {
 var sw = Stopwatch.StartNew();
 bool identical = method(file1, file2);
 Console.WriteLine("Method: {0}, Identical: {1}. Elapsed: {2}", method.Method.Name, identical, sw.Elapsed);
 }
}

看完上述內容,你們掌握怎么在.NET CORE中比較兩個文件內容是否相同的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

勃利县| 合川市| 宜都市| 弥勒县| 南通市| 凤冈县| 固原市| 许昌市| 郁南县| 荔浦县| 温泉县| 安丘市| 莱西市| 东乡族自治县| 彭州市| 烟台市| 五峰| 神农架林区| 左云县| 呈贡县| 从化市| 新邵县| 仙游县| 泌阳县| 华容县| 若羌县| 汝城县| 隆化县| 永胜县| 铜陵市| 个旧市| 阳泉市| 呈贡县| 宜春市| 甘肃省| 四川省| 绥德县| 庄河市| 巴塘县| 拜城县| 新乡市|