Wie vergleicht man 2 Dateien schnell mit .NET?

Typische Ansätze empfehlen, die Binärdatei über FileStream zu lesen und Byte für Byte zu vergleichen.

  • Wäre ein Prüfsummenvergleich wie CRC schneller?
  • Gibt es .NET-Bibliotheken, die eine Prüfsumme für eine Datei generieren können?

Ein Prüfsummenvergleich ist wahrscheinlich langsamer als ein Byte-für-Byte-Vergleich.

Um eine Prüfsumme zu generieren, müssen Sie jedes Byte der Datei laden und die Verarbeitung durchführen. Sie müssen dies dann für die zweite Datei tun. Die Verarbeitung wird fast definitiv langsamer sein als die Vergleichsprüfung.

Wie zum Erzeugen einer Prüfsumme: Sie können dies leicht mit den Kryptografieklassen tun. Hier ist ein kurzes Beispiel zum Generieren einer MD5-Prüfsumme mit C #.

Eine Prüfsumme kann jedoch schneller sein und ist sinnvoller, wenn Sie die Prüfsumme des “Test” – oder “Basis” -Falls vorberechnen können. Wenn Sie eine vorhandene Datei haben und prüfen, ob eine neue Datei die gleiche wie die vorhandene Datei ist, würde die Vorberechnung der Prüfsumme für Ihre “existierende” Datei bedeuten, dass Sie das DiskIO nur einmal auf der Festplatte ausführen müssen neue Datei. Dies wäre wahrscheinlich schneller als ein Byte-für-Byte-Vergleich.

Die langsamste Methode besteht darin, zwei Dateien byteweise zu vergleichen. Der schnellste Weg, den ich mir vorstellen konnte, ist ein ähnlicher Vergleich, aber statt eines Byte zu einem Zeitpunkt würden Sie ein Array von Bytes verwenden, die an Int64 angepasst sind, und dann die resultierenden Zahlen vergleichen.

Folgendes habe ich mir ausgedacht:

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; } 

In meinen Tests konnte ich sehen, dass dies ein einfaches ReadByte () -Szenario um fast 3: 1 übertraf. Im Durchschnitt über 1000 Läufe, habe ich diese Methode bei 1063ms, und die Methode unten (einfacher byteweise Vergleich) bei 3031ms. Hashing kam immer mit einer Subsekunde von durchschnittlich 865ms zurück. Dieser Test wurde mit einer ~ 100MB Videodatei durchgeführt.

Hier sind die ReadByte und Hash-Methoden, die ich für Vergleichszwecke verwendet habe:

  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 

Zusätzlich zu Reed Copseys Antwort:

  • Im schlimmsten Fall sind die beiden Dateien identisch. In diesem Fall ist es am besten, die Dateien Byte für Byte zu vergleichen.

  • Wenn die zwei Dateien nicht identisch sind, können Sie die Dinge etwas beschleunigen, indem Sie früher feststellen, dass sie nicht identisch sind.

Zum Beispiel, wenn die zwei Dateien unterschiedliche Länge haben, dann wissen Sie, dass sie nicht identisch sein können, und Sie müssen nicht einmal ihren tatsächlichen Inhalt vergleichen.

Wenn Sie sich entscheiden, dass Sie wirklich einen vollständigen Byte-für-Byte-Vergleich benötigen (siehe andere Antworten zur Diskussion des Hashing), dann lautet die einzeilige Lösung:

 bool bFilesAreEqual = File.ReadAllBytes(path1).SequenceEqual(File.ReadAllBytes(path2)); 

Im Gegensatz zu anderen geposteten Antworten funktioniert dies für jede Art von Datei korrekt : Binär, Text, Medien, ausführbare Datei usw., aber als vollständiger Binärvergleich , Dateien, die sich nur in “unwichtigen” Arten unterscheiden (wie BOM , Zeilen- Endung , Zeichenkodierung , Medienmetadaten, Leerzeichen, Padding, Quellcode-Kommentare usw.) werden immer als ungleich betrachtet .

Dieser Code lädt beide Dateien vollständig in den Speicher und sollte daher nicht zum Vergleichen von gigantischen Dateien verwendet werden. Abgesehen von dieser Überlegung ist das Vollladen nicht wirklich eine Strafe; in der Tat könnte dies eine optimale .NET-Lösung für Dateigrößen sein, die weniger als 85K betragen sollen , da kleine Zuordnungen in .NET sehr billig sind und wir die Dateileistung und -optimierung maximal an die CLR / BCL delegieren.

Für solche Workaday-Szenarien sind Bedenken hinsichtlich der performance eines Byte-für-Byte-Vergleichs über LINQ Enumeratoren (wie hier gezeigt) irrelevant, da ein Treffer der Platte für Datei-I / O die Vorteile von die verschiedenen Speichervergleichsalternativen. Zum Beispiel, obwohl SequenceEqual tatsächlich uns die “Optimierung” des Aufgebens bei der ersten Nichtübereinstimmung gibt , ist dies kaum von Bedeutung, nachdem bereits der Inhalt der Dateien abgerufen wurde, wobei jeder vollständig notwendig ist, um den Abgleich zu bestätigen.

Auf der anderen Seite enthält der obige Code keinen eifrigen Abbruch für unterschiedlich große Dateien , die einen fühlbaren (möglicherweise messbaren) performancesunterschied liefern können . Dieser ist greifbar, da, während die Dateilänge in der WIN32_FILE_ATTRIBUTE_DATA Struktur verfügbar ist (die sowieso für jeden Dateizugriff abgerufen werden muss), ein fortgesetzter Zugriff auf den Inhalt der Datei einen völlig anderen Abruf erfordert, der möglicherweise vermieden werden kann. Wenn Sie darüber besorgt sind, wird die Lösung zwei Zeilen:

  // slight optimization over the code shown above bool bFilesAreEqual = new FileInfo(path1).Length == new FileInfo(path2).Length && File.ReadAllBytes(path1).SequenceEqual(File.ReadAllBytes(path2)); 

Sie könnten dies auch erweitern, um die sekundären Abrufe zu vermeiden, wenn die (äquivalenten) FileInfo beide als null (nicht gezeigt) gefunden werden und / oder um zu vermeiden, dass jede FileInfo zweimal gebildet wird (ebenfalls nicht gezeigt).

Die einzige Sache, die einen Prüfsummenvergleich etwas schneller als ein Byte-für-Byte-Vergleich machen könnte, ist die Tatsache, dass Sie jeweils eine Datei lesen, was die Suchzeit für den Plattenkopf etwas verkürzt. Dieser geringe Gewinn kann jedoch sehr gut durch die zusätzliche Zeit der Berechnung des Hash aufgefressen werden.

Auch ein Prüfsummenvergleich hat natürlich nur dann die Chance, schneller zu sein, wenn die Dateien identisch sind. Wenn dies nicht der Fall ist, würde ein Byte-für-Byte-Vergleich bei der ersten Differenz enden, wodurch er viel schneller wird.

Sie sollten auch bedenken, dass ein Hash-Code-Vergleich Ihnen nur sagt, dass es sehr wahrscheinlich ist, dass die Dateien identisch sind. Um 100% sicher zu sein, müssen Sie einen byteweisen Vergleich durchführen.

Wenn der Hash-Code beispielsweise 32 Bit beträgt, sind Sie bei 99,999999998% sicher, dass die Dateien identisch sind, wenn die Hash-Codes übereinstimmen. Das ist fast 100%, aber wenn Sie wirklich 100% Sicherheit brauchen, ist es das nicht.

Es wird noch schneller, wenn Sie nicht in kleinen 8-Byte-Blöcken lesen, sondern eine Schleife um einen größeren Block lesen. Ich habe die durchschnittliche Vergleichszeit auf 1/4 reduziert.

  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; } } } } } 

Edit: Diese Methode würde nicht zum Vergleichen von Binärdateien funktionieren!

In .NET 4.0 verfügt die File class über die folgenden zwei neuen Methoden:

 public static IEnumerable ReadLines(string path) public static IEnumerable ReadLines(string path, Encoding encoding) 

Was bedeutet, dass Sie verwenden könnten:

 bool same = File.ReadLines(path1).SequenceEqual(File.ReadLines(path2)); 

Ehrlich gesagt, denke ich, dass Sie Ihren Suchbaum so weit wie möglich zurückschneiden müssen.

Dinge zu überprüfen, bevor Byte für Byte gehen:

  1. Sind die Größen gleich?
  2. Ist das letzte Byte in Datei A anders als Datei B?

Auch das Lesen großer Blöcke auf einmal ist effizienter, da Laufwerke sequentielle Bytes schneller lesen. Ein Byte-für-Byte-Durchlauf verursacht nicht nur wesentlich mehr Systemaufrufe, sondern bewirkt auch, dass der Lesekopf einer herkömmlichen Festplatte öfter hin- und herläuft, wenn sich beide Dateien auf demselben Laufwerk befinden.

Lesen Sie Chunk A und Chunk B in einen Byte-Puffer und vergleichen Sie sie (verwenden Sie nicht Array.Equals, siehe Kommentare). Stimmen Sie die Größe der Blöcke so lange ab, bis Sie das Gefühl haben, dass es sich um einen guten Kompromiss zwischen Speicher und performance handelt. Sie können den Vergleich auch multi-threading durchführen, aber nicht die Lesevorgänge multithread.

Meine Experimente zeigen, dass es definitiv hilft, Stream.ReadByte () weniger oft aufzurufen, aber das Verwenden von BitConverter zum Paketieren von Bytes macht keinen großen Unterschied gegenüber dem Vergleichen von Bytes in einem Byte-Array.

Es ist also möglich, die Schleife “Math.Ceiling and iterations” im obigen Kommentar durch die einfachste zu ersetzen:

  for (int i = 0; i < count1; i++) { if (buffer1[i] != buffer2[i]) return false; } 

Ich denke, es hat damit zu tun, dass BitConverter.ToInt64 ein wenig Arbeit zu tun hat (überprüfen Sie die Argumente und dann die Bitverschiebung), bevor Sie es vergleichen, und das ist die gleiche Menge an Arbeit wie 8 Bytes in zwei Arrays vergleichen .

Eine weitere Verbesserung bei großen Dateien mit gleicher Länge besteht darin, die Dateien nicht sequentiell zu lesen, sondern mehr oder weniger zufällige Blöcke zu vergleichen.

Sie können mehrere Threads verwenden, indem Sie an verschiedenen Positionen in der Datei beginnen und entweder vorwärts oder rückwärts vergleichen.

Auf diese Weise können Sie Änderungen an der Mitte / am Ende der Datei schneller erkennen, als Sie mit einem sequenziellen Ansatz erhalten würden.

Wenn Sie nur zwei Dateien vergleichen müssen, wäre der schnellste Weg (in C weiß ich nicht, ob es auf .NET anwendbar ist)

  1. öffne beide Dateien f1, f2
  2. bekomme die entsprechende Dateilänge l1, l2
  3. wenn l1! = l2 sind die Dateien unterschiedlich; halt
  4. mmap () beide Dateien
  5. Verwenden Sie memcmp () in den ed-Dateien mmap ()

OTOH, wenn Sie herausfinden müssen, ob doppelte Dateien in einer Menge von N Dateien vorhanden sind, dann ist der schnellste Weg zweifellos ein Hash zu verwenden, um N-Wege-Bit-für-Bit-Vergleiche zu vermeiden.

Etwas (hoffentlich) einigermaßen effizient:

 public class FileCompare { public static bool FilesEqual(string fileName1, string fileName2) { return FilesEqual(new FileInfo(fileName1), new FileInfo(fileName2)); } ///  /// ///  ///  ///  /// 8kb seemed like a good default ///  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; } } } } ///  /// ///  ///  ///  ///  0 means compare entire arrays ///  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; } } 

Hier sind einige Utility-functionen, mit denen Sie feststellen können, ob zwei Dateien (oder zwei Streams) identische Daten enthalten.

Ich habe eine “schnelle” Version zur Verfügung gestellt, die multi-threaded ist, da sie Bytearrays (jeder Puffer gefüllt mit dem, was in jeder Datei gelesen wurde) in verschiedenen Threads unter Verwendung von Aufgaben vergleicht.

Wie erwartet, ist es viel schneller (etwa 3x schneller), aber es verbraucht mehr CPU (weil es multi-threaded ist) und mehr Speicher (weil es zwei Byte Array-Puffer pro Vergleichsthread benötigt).

  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 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>(); 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>(); 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; } 

Wenn die Dateien nicht zu groß sind, können Sie Folgendes verwenden:

 public static byte[] ComputeFileHash(string fileName) { using (var stream = File.OpenRead(fileName)) return System.Security.Cryptography.MD5.Create().ComputeHash(stream); } 

Es wird nur möglich sein, Hashes zu vergleichen, wenn die Hashes zum Speichern nützlich sind.

(Bearbeitet den Code zu etwas viel sauberer.)

Ich denke, es gibt Anwendungen, bei denen “Hash” schneller ist als Byte für Byte. Wenn Sie eine Datei mit anderen vergleichen müssen oder ein Thumbnail eines Fotos haben, das sich ändern kann. Es hängt davon ab, wo und wie es verwendet wird.

 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; } 

Hier können Sie herausfinden, was am schnellsten ist.

 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)); 

Optional können wir den Hash in einer database speichern.

Hoffe, das kann helfen

Noch eine andere Antwort, abgeleitet von @chsh. MD5 mit Usings und Shortcuts für Datei same, Datei nicht vorhanden und unterschiedliche Längen:

 ///  /// Performs an md5 on the content of both files and returns true if /// they match ///  /// first file /// second file /// true if the contents of the two files is the same, false otherwise 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; } } 

Meine Antwort ist eine Ableitung von @lars, behebt aber den Fehler im Aufruf von Stream.Read . Ich füge auch einige schnelle Pfadprüfungen hinzu, die andere Antworten hatten, und gebe validation ein. Kurz gesagt, dies sollte die Antwort sein:

 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; } } } } } } 

Oder wenn Sie super-toll sein wollen, können Sie die asynchrone Variante verwenden:

 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 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 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 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; } } } } } } 

Dies hat sich als gut erwiesen, indem ich zuerst die Länge vergleiche, ohne Daten zu lesen und dann die gelesene Bytefolge zu vergleichen

 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))); }