回答:
チェックサムの比較は、バイト単位の比較よりも遅くなる可能性があります。
チェックサムを生成するには、ファイルの各バイトをロードして、処理を実行する必要があります。次に、2番目のファイルでこれを行う必要があります。処理は、ほぼ間違いなく比較チェックよりも遅くなります。
チェックサムの生成については、暗号化クラスを使用して簡単に行うことができます。これは、C#でMD5チェックサムを生成する短い例です。
ただし、「テスト」または「ベース」のケースのチェックサムを事前に計算できる場合は、チェックサムの方が高速であり、より意味があります。既存のファイルがあり、新しいファイルが既存のファイルと同じであるかどうかを確認している場合、「既存の」ファイルのチェックサムを事前に計算すると、DiskIOを1回実行するだけで済みます。新しいファイル。これは、バイトごとの比較よりも高速です。
最も遅い方法は、2つのファイルをバイト単位で比較することです。私が思いつくことができた最速は同様の比較ですが、一度に1バイトではなく、Int64サイズのバイト配列を使用して、結果の数値を比較します。
これが私が思いついたものです:
const int BYTES_TO_READ = sizeof(Int64);
static bool FilesAreEqual(FileInfo first, FileInfo second)
{
if (first.Length != second.Length)
return false;
if (string.Equals(first.FullName, second.FullName, StringComparison.OrdinalIgnoreCase))
return true;
int iterations = (int)Math.Ceiling((double)first.Length / BYTES_TO_READ);
using (FileStream fs1 = first.OpenRead())
using (FileStream fs2 = second.OpenRead())
{
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);
if (BitConverter.ToInt64(one,0) != BitConverter.ToInt64(two,0))
return false;
}
}
return true;
}
私のテストでは、これがほぼ3:1で簡単なReadByte()シナリオよりも優れていることを確認できました。平均で1000回の実行で、このメソッドは1063ミリ秒で、以下のメソッド(バイト単位の単純な比較)は3031ミリ秒で得られました。ハッシュは常に平均約865ミリ秒で1秒未満に戻りました。このテストでは、約100MBのビデオファイルを使用しました。
以下は、比較のために使用したReadByteとハッシュメソッドです。
static bool FilesAreEqual_OneByte(FileInfo first, FileInfo second)
{
if (first.Length != second.Length)
return false;
if (string.Equals(first.FullName, second.FullName, StringComparison.OrdinalIgnoreCase))
return true;
using (FileStream fs1 = first.OpenRead())
using (FileStream fs2 = second.OpenRead())
{
for (int i = 0; i < first.Length; i++)
{
if (fs1.ReadByte() != fs2.ReadByte())
return false;
}
}
return true;
}
static bool FilesAreEqual_Hash(FileInfo first, FileInfo second)
{
byte[] firstHash = MD5.Create().ComputeHash(first.OpenRead());
byte[] secondHash = MD5.Create().ComputeHash(second.OpenRead());
for (int i=0; i<firstHash.Length; i++)
{
if (firstHash[i] != secondHash[i])
return false;
}
return true;
}
FilesAreEqual_Hash
この方法は、持っている必要がありusing
、両方のファイルに似すぎストリームReadByte
方法それ以外の場合は、両方のファイルにハングアップします。
FileStream.Read()
実際には、要求された数よりも少ないバイトを読み取る可能性があることに注意してください。StreamReader.ReadBlock()
代わりに使用する必要があります。
あなたがいる場合か、あなたが本当に必要とする決定フルバイト単位の比較を(ハッシュの議論のための他の回答を参照)、その後、最も簡単な解決策は以下のとおりです。
• System.IO.FileInfo
インスタンスの場合:
public static bool AreFileContentsEqual(FileInfo fi1, FileInfo fi2) =>
fi1.Length == fi2.Length &&
(fi1.Length == 0 || File.ReadAllBytes(fi1.FullName).SequenceEqual(
File.ReadAllBytes(fi2.FullName)));
• System.String
パス名:
public static bool AreFileContentsEqual(String path1, String path2) =>
AreFileContentsEqual(new FileInfo(path1), new FileInfo(path2));
他のいくつかの投稿された回答とは異なり、これはバイナリ、テキスト、メディア、実行可能ファイルなど、あらゆる種類のファイルに対して決定的に正しいですが、完全なバイナリ比較として、「重要でない」方法のみが異なるファイル(BOM、行など) -終了、文字エンコード、メディアメタデータ、空白、パディング、ソースコードコメントなど)は常に等しくないと見なされます。
このコードは両方のファイルをメモリに完全にロードするため、本当に巨大なファイルを比較するために使用しないでください。その重要な警告を超えて、フルロードは.NET GCの設計を考えると実際には不利ではありません(これは根本的に最適化されており、短期間の割り当てを非常に安価に保つためです)。実際、ファイルサイズが予想される場合でも最適です。未満であることを85K(ここで示したように)ユーザーコードの最小値を使用しているのですることは、最大限のファイルのパフォーマンスの問題を委任意味CLR
、BCL
およびJIT
最新の設計技術、システムコード、および適応ランタイムの最適化(例えば)からの利益のために。
また、このような仕事日のシナリオを介してバイト単位の比較のパフォーマンスについての懸念のためにLINQ
ディスクを打つので、列挙子(ここで示したように)、議論の余地があるすべてのファイルI / Oのためには、数桁、利益を小さく見せますさまざまなメモリ比較の選択肢の。例えば、たとえSequenceEqual
ん実際に私たちの「最適化」を与える最初の不一致に放棄し、これはほとんどのファイルの内容をすでに持っフェッチした後、それぞれ完全に必要な一致を確認するために重要ありません..
リード・コプシーの答えに加えて:
最悪のケースは、2つのファイルが同一である場合です。この場合、ファイルをバイト単位で比較するのが最善です。
2つのファイルが同一でない場合は、それらが同一でないことをより早く検出することで、処理を少し高速化できます。
たとえば、2つのファイルの長さが異なる場合は、それらを同一にすることはできず、実際の内容を比較する必要もありません。
小さな8バイトのチャンクではなく、ループを入れて大きなチャンクを読み取ると、さらに速くなります。平均比較時間を1/4に短縮しました。
public static bool FilesContentsAreEqual(FileInfo fileInfo1, FileInfo fileInfo2)
{
bool result;
if (fileInfo1.Length != fileInfo2.Length)
{
result = false;
}
else
{
using (var file1 = fileInfo1.OpenRead())
{
using (var file2 = fileInfo2.OpenRead())
{
result = StreamsContentsAreEqual(file1, file2);
}
}
}
return result;
}
private static bool StreamsContentsAreEqual(Stream stream1, Stream stream2)
{
const int bufferSize = 1024 * sizeof(Int64);
var buffer1 = new byte[bufferSize];
var buffer2 = new byte[bufferSize];
while (true)
{
int count1 = stream1.Read(buffer1, 0, bufferSize);
int count2 = stream2.Read(buffer2, 0, bufferSize);
if (count1 != count2)
{
return false;
}
if (count1 == 0)
{
return true;
}
int iterations = (int)Math.Ceiling((double)count1 / sizeof(Int64));
for (int i = 0; i < iterations; i++)
{
if (BitConverter.ToInt64(buffer1, i * sizeof(Int64)) != BitConverter.ToInt64(buffer2, i * sizeof(Int64)))
{
return false;
}
}
}
}
}
count1 != count2
は正しくありません。Stream.Read()
さまざまな理由により、指定した数よりも少ない数を返す場合があります。
Int64
ブロックを保持することを保証するために、次のようにサイズを計算することができますconst int bufferSize = 1024 * sizeof(Int64)
。
チェックサムの比較をバイト単位の比較よりもわずかに速くする可能性があるのは、一度に1つのファイルを読み取るため、ディスクヘッドのシーク時間がいくらか短縮されるという事実だけです。ただし、そのわずかな利益は、ハッシュを計算する追加の時間によって完全に消費される可能性があります。
また、当然のことながら、チェックサムの比較では、ファイルが同一である場合にのみ高速化する可能性があります。そうでない場合、バイトごとの比較は最初の違いで終了し、はるかに高速になります。
また、ハッシュコードの比較は、ファイルが同一である可能性が非常に高いことを示しているだけであることも考慮する必要があります。100%確実にするには、バイトごとの比較を行う必要があります。
たとえば、ハッシュコードが32ビットの場合、ハッシュコードが一致すれば、ファイルが同じであることが99.99999998%確実です。それは100%に近いですが、100%の確実性が本当に必要な場合は、それだけではありません。
1 - (1 / (2^32))
、これはコンピューティングから来ています。これは、任意の単一のファイルが特定の32ビットハッシュを持つ確率です。2つの異なるファイルが同じハッシュを持つ確率は同じです。最初のファイルは「所定の」ハッシュ値を提供するため、他のファイルがその値と一致するかどうかを考慮するだけで済みます。64ビットおよび128ビットのハッシュの可能性は、そのような計り知れない数に関係があるかのように、99.999999999999999994%と99.9999999999999999999999999999999999999997%(それぞれ)に減少します。
編集:この方法はバイナリファイルの比較には機能しません!
.NET 4.0では、File
クラスに次の2つの新しいメソッドがあります。
public static IEnumerable<string> ReadLines(string path)
public static IEnumerable<string> ReadLines(string path, Encoding encoding)
つまり、以下を使用できます。
bool same = File.ReadLines(path1).SequenceEqual(File.ReadLines(path2));
正直なところ、できる限り検索ツリーを切り詰める必要があると思います。
バイトごとに進む前に確認すること:
また、ドライブがシーケンシャルバイトをより速く読み取るため、一度に大きなブロックを読み取る方が効率的です。バイトごとに移動すると、システムコールがはるかに増えるだけでなく、両方のファイルが同じドライブ上にある場合、従来のハードドライブの読み取りヘッドが頻繁にやり取りするようになります。
チャンクAとチャンクBをバイトバッファーに読み込み、それらを比較します(Array.Equalsは使用しないでください。コメントを参照してください)。メモリとパフォーマンスの適切なトレードオフであると感じるまで、ブロックのサイズを調整します。比較をマルチスレッド化することもできますが、ディスクの読み取りをマルチスレッド化しないでください。
私の答えは@larsの派生物ですが、への呼び出しのバグを修正していますStream.Read
。また、他の回答の高速パスチェックと入力検証も追加します。要するに、これはする必要があります答え:
using System;
using System.IO;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
var fi1 = new FileInfo(args[0]);
var fi2 = new FileInfo(args[1]);
Console.WriteLine(FilesContentsAreEqual(fi1, fi2));
}
public static bool FilesContentsAreEqual(FileInfo fileInfo1, FileInfo fileInfo2)
{
if (fileInfo1 == null)
{
throw new ArgumentNullException(nameof(fileInfo1));
}
if (fileInfo2 == null)
{
throw new ArgumentNullException(nameof(fileInfo2));
}
if (string.Equals(fileInfo1.FullName, fileInfo2.FullName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (fileInfo1.Length != fileInfo2.Length)
{
return false;
}
else
{
using (var file1 = fileInfo1.OpenRead())
{
using (var file2 = fileInfo2.OpenRead())
{
return StreamsContentsAreEqual(file1, file2);
}
}
}
}
private static int ReadFullBuffer(Stream stream, byte[] buffer)
{
int bytesRead = 0;
while (bytesRead < buffer.Length)
{
int read = stream.Read(buffer, bytesRead, buffer.Length - bytesRead);
if (read == 0)
{
// Reached end of stream.
return bytesRead;
}
bytesRead += read;
}
return bytesRead;
}
private static bool StreamsContentsAreEqual(Stream stream1, Stream stream2)
{
const int bufferSize = 1024 * sizeof(Int64);
var buffer1 = new byte[bufferSize];
var buffer2 = new byte[bufferSize];
while (true)
{
int count1 = ReadFullBuffer(stream1, buffer1);
int count2 = ReadFullBuffer(stream2, buffer2);
if (count1 != count2)
{
return false;
}
if (count1 == 0)
{
return true;
}
int iterations = (int)Math.Ceiling((double)count1 / sizeof(Int64));
for (int i = 0; i < iterations; i++)
{
if (BitConverter.ToInt64(buffer1, i * sizeof(Int64)) != BitConverter.ToInt64(buffer2, i * sizeof(Int64)))
{
return false;
}
}
}
}
}
}
または、超素晴らしいものにしたい場合は、非同期バリアントを使用できます。
using System;
using System.IO;
using System.Threading.Tasks;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
var fi1 = new FileInfo(args[0]);
var fi2 = new FileInfo(args[1]);
Console.WriteLine(FilesContentsAreEqualAsync(fi1, fi2).GetAwaiter().GetResult());
}
public static async Task<bool> FilesContentsAreEqualAsync(FileInfo fileInfo1, FileInfo fileInfo2)
{
if (fileInfo1 == null)
{
throw new ArgumentNullException(nameof(fileInfo1));
}
if (fileInfo2 == null)
{
throw new ArgumentNullException(nameof(fileInfo2));
}
if (string.Equals(fileInfo1.FullName, fileInfo2.FullName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (fileInfo1.Length != fileInfo2.Length)
{
return false;
}
else
{
using (var file1 = fileInfo1.OpenRead())
{
using (var file2 = fileInfo2.OpenRead())
{
return await StreamsContentsAreEqualAsync(file1, file2).ConfigureAwait(false);
}
}
}
}
private static async Task<int> ReadFullBufferAsync(Stream stream, byte[] buffer)
{
int bytesRead = 0;
while (bytesRead < buffer.Length)
{
int read = await stream.ReadAsync(buffer, bytesRead, buffer.Length - bytesRead).ConfigureAwait(false);
if (read == 0)
{
// Reached end of stream.
return bytesRead;
}
bytesRead += read;
}
return bytesRead;
}
private static async Task<bool> StreamsContentsAreEqualAsync(Stream stream1, Stream stream2)
{
const int bufferSize = 1024 * sizeof(Int64);
var buffer1 = new byte[bufferSize];
var buffer2 = new byte[bufferSize];
while (true)
{
int count1 = await ReadFullBufferAsync(stream1, buffer1).ConfigureAwait(false);
int count2 = await ReadFullBufferAsync(stream2, buffer2).ConfigureAwait(false);
if (count1 != count2)
{
return false;
}
if (count1 == 0)
{
return true;
}
int iterations = (int)Math.Ceiling((double)count1 / sizeof(Int64));
for (int i = 0; i < iterations; i++)
{
if (BitConverter.ToInt64(buffer1, i * sizeof(Int64)) != BitConverter.ToInt64(buffer2, i * sizeof(Int64)))
{
return false;
}
}
}
}
}
}
私の実験では、Stream.ReadByte()の呼び出し回数を減らすことは間違いなく役立つことを示していますが、BitConverterを使用してバイトをパッケージ化しても、バイト配列内のバイトの比較とそれほど違いはありません。
したがって、上記のコメントの「Math.Ceiling and iterations」ループを最も単純なものに置き換えることができます。
for (int i = 0; i < count1; i++)
{
if (buffer1[i] != buffer2[i])
return false;
}
BitConverter.ToInt64は、比較する前に少し作業(引数をチェックしてからビットシフトを実行)する必要があり、2つの配列の8バイトを比較するのと同じ量の作業になることに関係していると思います。
ファイルが大きすぎない場合は、以下を使用できます。
public static byte[] ComputeFileHash(string fileName)
{
using (var stream = File.OpenRead(fileName))
return System.Security.Cryptography.MD5.Create().ComputeHash(stream);
}
ハッシュが保存に役立つ場合のみ、ハッシュを比較できます。
(コードをよりクリーンなものに編集しました。)
同じ長さの大きなファイルのもう1つの改善点は、ファイルを順番に読み取るのではなく、ランダムブロックを比較することです。
複数のスレッドを使用して、ファイル内の異なる位置から開始し、前方または後方のいずれかを比較できます。
このようにして、ファイルの中央/末尾で変更を検出できます。シーケンシャルアプローチを使用した場合よりも速く検出できます。
(うまくいけば)合理的に効率的なもの:
public class FileCompare
{
public static bool FilesEqual(string fileName1, string fileName2)
{
return FilesEqual(new FileInfo(fileName1), new FileInfo(fileName2));
}
/// <summary>
///
/// </summary>
/// <param name="file1"></param>
/// <param name="file2"></param>
/// <param name="bufferSize">8kb seemed like a good default</param>
/// <returns></returns>
public static bool FilesEqual(FileInfo file1, FileInfo file2, int bufferSize = 8192)
{
if (!file1.Exists || !file2.Exists || file1.Length != file2.Length) return false;
var buffer1 = new byte[bufferSize];
var buffer2 = new byte[bufferSize];
using (var stream1 = file1.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var stream2 = file2.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
{
while (true)
{
var bytesRead1 = stream1.Read(buffer1, 0, bufferSize);
var bytesRead2 = stream2.Read(buffer2, 0, bufferSize);
if (bytesRead1 != bytesRead2) return false;
if (bytesRead1 == 0) return true;
if (!ArraysEqual(buffer1, buffer2, bytesRead1)) return false;
}
}
}
}
/// <summary>
///
/// </summary>
/// <param name="array1"></param>
/// <param name="array2"></param>
/// <param name="bytesToCompare"> 0 means compare entire arrays</param>
/// <returns></returns>
public static bool ArraysEqual(byte[] array1, byte[] array2, int bytesToCompare = 0)
{
if (array1.Length != array2.Length) return false;
var length = (bytesToCompare == 0) ? array1.Length : bytesToCompare;
var tailIdx = length - length % sizeof(Int64);
//check in 8 byte chunks
for (var i = 0; i < tailIdx; i += sizeof(Int64))
{
if (BitConverter.ToInt64(array1, i) != BitConverter.ToInt64(array2, i)) return false;
}
//check the remainder of the array, always shorter than 8 bytes
for (var i = tailIdx; i < length; i++)
{
if (array1[i] != array2[i]) return false;
}
return true;
}
}
2つのファイル(または2つのストリーム)に同一のデータが含まれているかどうかを判別できるいくつかのユーティリティ関数を次に示します。
タスクを使用して異なるスレッドでバイト配列(各ファイルで読み取られたものから各バッファーが満たされた)を比較するため、マルチスレッドの「高速」バージョンを提供しました。
予想どおり、はるかに高速です(約3倍高速)が、CPU(マルチスレッド化されているため)とメモリ(比較スレッドごとに2バイトの配列バッファが必要なため)を多く消費します。
public static bool AreFilesIdenticalFast(string path1, string path2)
{
return AreFilesIdentical(path1, path2, AreStreamsIdenticalFast);
}
public static bool AreFilesIdentical(string path1, string path2)
{
return AreFilesIdentical(path1, path2, AreStreamsIdentical);
}
public static bool AreFilesIdentical(string path1, string path2, Func<Stream, Stream, bool> areStreamsIdentical)
{
if (path1 == null)
throw new ArgumentNullException(nameof(path1));
if (path2 == null)
throw new ArgumentNullException(nameof(path2));
if (areStreamsIdentical == null)
throw new ArgumentNullException(nameof(path2));
if (!File.Exists(path1) || !File.Exists(path2))
return false;
using (var thisFile = new FileStream(path1, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var valueFile = new FileStream(path2, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
if (valueFile.Length != thisFile.Length)
return false;
if (!areStreamsIdentical(thisFile, valueFile))
return false;
}
}
return true;
}
public static bool AreStreamsIdenticalFast(Stream stream1, Stream stream2)
{
if (stream1 == null)
throw new ArgumentNullException(nameof(stream1));
if (stream2 == null)
throw new ArgumentNullException(nameof(stream2));
const int bufsize = 80000; // 80000 is below LOH (85000)
var tasks = new List<Task<bool>>();
do
{
// consumes more memory (two buffers for each tasks)
var buffer1 = new byte[bufsize];
var buffer2 = new byte[bufsize];
int read1 = stream1.Read(buffer1, 0, buffer1.Length);
if (read1 == 0)
{
int read3 = stream2.Read(buffer2, 0, 1);
if (read3 != 0) // not eof
return false;
break;
}
// both stream read could return different counts
int read2 = 0;
do
{
int read3 = stream2.Read(buffer2, read2, read1 - read2);
if (read3 == 0)
return false;
read2 += read3;
}
while (read2 < read1);
// consumes more cpu
var task = Task.Run(() =>
{
return IsSame(buffer1, buffer2);
});
tasks.Add(task);
}
while (true);
Task.WaitAll(tasks.ToArray());
return !tasks.Any(t => !t.Result);
}
public static bool AreStreamsIdentical(Stream stream1, Stream stream2)
{
if (stream1 == null)
throw new ArgumentNullException(nameof(stream1));
if (stream2 == null)
throw new ArgumentNullException(nameof(stream2));
const int bufsize = 80000; // 80000 is below LOH (85000)
var buffer1 = new byte[bufsize];
var buffer2 = new byte[bufsize];
var tasks = new List<Task<bool>>();
do
{
int read1 = stream1.Read(buffer1, 0, buffer1.Length);
if (read1 == 0)
return stream2.Read(buffer2, 0, 1) == 0; // check not eof
// both stream read could return different counts
int read2 = 0;
do
{
int read3 = stream2.Read(buffer2, read2, read1 - read2);
if (read3 == 0)
return false;
read2 += read3;
}
while (read2 < read1);
if (!IsSame(buffer1, buffer2))
return false;
}
while (true);
}
public static bool IsSame(byte[] bytes1, byte[] bytes2)
{
if (bytes1 == null)
throw new ArgumentNullException(nameof(bytes1));
if (bytes2 == null)
throw new ArgumentNullException(nameof(bytes2));
if (bytes1.Length != bytes2.Length)
return false;
for (int i = 0; i < bytes1.Length; i++)
{
if (bytes1[i] != bytes2[i])
return false;
}
return true;
}
「ハッシュ」がバイト単位で比較するよりも速いアプリケーションがあると思います。ファイルを他のユーザーと比較する必要がある場合、または変更可能な写真のサムネイルがある場合。どこでどのように使用しているかによります。
private bool CompareFilesByte(string file1, string file2)
{
using (var fs1 = new FileStream(file1, FileMode.Open))
using (var fs2 = new FileStream(file2, FileMode.Open))
{
if (fs1.Length != fs2.Length) return false;
int b1, b2;
do
{
b1 = fs1.ReadByte();
b2 = fs2.ReadByte();
if (b1 != b2 || b1 < 0) return false;
}
while (b1 >= 0);
}
return true;
}
private string HashFile(string file)
{
using (var fs = new FileStream(file, FileMode.Open))
using (var reader = new BinaryReader(fs))
{
var hash = new SHA512CryptoServiceProvider();
hash.ComputeHash(reader.ReadBytes((int)file.Length));
return Convert.ToBase64String(hash.Hash);
}
}
private bool CompareFilesWithHash(string file1, string file2)
{
var str1 = HashFile(file1);
var str2 = HashFile(file2);
return str1 == str2;
}
ここでは、最速のものを入手できます。
var sw = new Stopwatch();
sw.Start();
var compare1 = CompareFilesWithHash(receiveLogPath, logPath);
sw.Stop();
Debug.WriteLine(string.Format("Compare using Hash {0}", sw.ElapsedTicks));
sw.Reset();
sw.Start();
var compare2 = CompareFilesByte(receiveLogPath, logPath);
sw.Stop();
Debug.WriteLine(string.Format("Compare byte-byte {0}", sw.ElapsedTicks));
オプションで、ハッシュをデータベースに保存できます。
これが役に立てば幸い
@chshから派生したさらに別の答え。同じファイルの使用法とショートカットを使用したMD5、ファイルが存在せず、長さが異なる:
/// <summary>
/// Performs an md5 on the content of both files and returns true if
/// they match
/// </summary>
/// <param name="file1">first file</param>
/// <param name="file2">second file</param>
/// <returns>true if the contents of the two files is the same, false otherwise</returns>
public static bool IsSameContent(string file1, string file2)
{
if (file1 == file2)
return true;
FileInfo file1Info = new FileInfo(file1);
FileInfo file2Info = new FileInfo(file2);
if (!file1Info.Exists && !file2Info.Exists)
return true;
if (!file1Info.Exists && file2Info.Exists)
return false;
if (file1Info.Exists && !file2Info.Exists)
return false;
if (file1Info.Length != file2Info.Length)
return false;
using (FileStream file1Stream = file1Info.OpenRead())
using (FileStream file2Stream = file2Info.OpenRead())
{
byte[] firstHash = MD5.Create().ComputeHash(file1Stream);
byte[] secondHash = MD5.Create().ComputeHash(file2Stream);
for (int i = 0; i < firstHash.Length; i++)
{
if (i>=secondHash.Length||firstHash[i] != secondHash[i])
return false;
}
return true;
}
}
if (i>=secondHash.Length ...
どのような状況下では、2つのMD5ハッシュは、異なる長さでしょうか?
これは、最初にデータを読み取らずに長さを比較し、次に読み取ったバイトシーケンスを比較することでうまく機能することがわかりました
private static bool IsFileIdentical(string a, string b)
{
if (new FileInfo(a).Length != new FileInfo(b).Length) return false;
return (File.ReadAllBytes(a).SequenceEqual(File.ReadAllBytes(b)));
}