web-dev-qa-db-de.com

Wie kann man 2 Dateien schnell mit .NET vergleichen?

Typische Ansätze Es wird empfohlen, 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?
110
Robinicks

Ein Prüfsummenvergleich ist höchstwahrscheinlich 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 in der zweiten Datei tun. Die Verarbeitung wird fast definitiv langsamer sein als die Vergleichsprüfung.

Zum Erstellen einer Prüfsumme: Dies ist mit den Kryptographieklassen problemlos möglich. Hier ist ein kurzes Beispiel für die Erstellung einer MD5-Prüfsumme mit C #.

Eine Prüfsumme kann jedoch schneller und sinnvoller sein, wenn Sie die Prüfsumme des "Test" - oder "Basis" -Falls vorberechnen können. Wenn Sie über eine vorhandene Datei verfügen und prüfen, ob eine neue Datei mit der vorhandenen identisch ist, müssen Sie die Prüfsumme für Ihre "vorhandene" Datei vorberechnen, indem Sie die DiskIO-Operation nur einmal durchführen neue Datei. Dies wäre wahrscheinlich schneller als ein Byte-für-Byte-Vergleich.

103
Reed Copsey

Die langsamste Möglichkeit ist, zwei Dateien Byte für Byte zu vergleichen. Der schnellste, den ich erzielen konnte, ist ein ähnlicher Vergleich, aber statt eines Bytes zu einem Zeitpunkt würden Sie ein Array von Bytes mit der Größe Int64 verwenden 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 feststellen, dass dieses Ergebnis ein geradliniges ReadByte () - Szenario um fast 3: 1 übertrifft. Im Durchschnitt über 1000 Durchläufe erhielt ich diese Methode bei 1063 ms und die Methode unten (einfacher Byte-für-Byte-Vergleich) bei 3031 ms. Das Hashing kam mit einem Durchschnitt von 865ms immer unter der Sekunde zurück. Dieser Test wurde mit einer ~ 100 MB Videodatei durchgeführt.

Hier sind die ReadByte- und Hash-Methoden, die ich zum Vergleich 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<firstHash.Length; i++)
        {
            if (firstHash[i] != secondHash[i])
                return false;
        }
        return true;
    }
117
chsh

Wenn Sie d̲o̲ entscheiden, dass Sie wirklich einen vollständigen byteweisen Vergleich benötigen (siehe andere Antworten zur Diskussion von Hashing), dann Leitungslösung ist:

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

Im Gegensatz zu einigen anderen Antworten funktioniert dies korrekt für Dateien aller Art: Binärdateien, Textdateien, Medien, ausführbare Dateien usw., jedoch als full = binär Vergleich , Dateien, die sich auf "unwichtige" Weise unterscheiden nur (wie BOM , Zeilenende , Zeichencodierung , Medienmetadaten, Leerzeichen, Auffüllung, Quellcodekommentare usw.) werden immer berücksichtigt ungleich .

Dieser Code lädt beide Dateien vollständig in den Speicher, daher sollte er nicht zum Vergleichen von gigantic Dateien verwendet werden. Abgesehen von dieser Überlegung ist das vollständige Laden keine wirkliche Strafe. Tatsächlich könnte dies eine optimale .NET-Lösung für Dateigrößen sein, die voraussichtlich unter 85 KB liegen, da kleine Zuordnungen in .NET sehr billig sind und der obige Code die Datei maximal delegiert Performance und Optimierung des CLR/BCL.

Darüber hinaus sind in solchen Workaday-Szenarien Bedenken hinsichtlich der Leistung des byteweisen Vergleichs über LINQ -Enumeratoren (wie hier gezeigt) unbegründet, da die Festplatte a̲t̲ a̲l̲l̲ für Datei-E/A wird die Vorteile der verschiedenen Alternativen zum Vergleich des Speichers um mehrere Größenordnungen in den Schatten stellen. Zum Beispiel, obwohl SequenceEqual does uns tatsächlich die "Optimierung" des Abbruchs bei der ersten Nichtübereinstimmung gibt, spielt dies kaum eine Rolle Nachdem Sie den Inhalt der Dateien bereits abgerufen haben, ist jeder vollständig erforderlich, um die Übereinstimmung zu bestätigen.

Andererseits enthält der obige Code nicht eifrigen Abbruch für unterschiedlich große Dateien , die können = einen spürbaren (möglicherweise messbaren) Leistungsunterschied bereitstellen. Diese ist greifbar, weil die Dateilänge in der Struktur WIN32_FILE_ATTRIBUTE_DATA Verfügbar ist (die ohnehin erst abgerufen werden muss, wenn auf die Inhalte der Datei zugegriffen werden soll). Dies erfordert jedoch einen völlig anderen Abruf, der möglicherweise vermieden werden kann . Wenn Sie darüber besorgt sind, besteht die Lösung aus zwei Zeilen:

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

Sie können dies auch erweitern, um die sekundären Abrufe zu vermeiden, wenn festgestellt wird, dass die (äquivalenten) Length -Werte beide Null sind (nicht gezeigt), und/oder um zu vermeiden, dass jedes FileInfo zweimal erstellt wird (auch nicht gezeigt) ).

36
Glenn Slayden

Zusätzlich zu Reed Copsey s 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 beiden Dateien nicht identisch sind, können Sie etwas beschleunigen, indem Sie früher erkennen, dass sie nicht identisch sind.

Wenn die beiden Dateien beispielsweise unterschiedlich lang sind, wissen Sie, dass sie nicht identisch sein können, und Sie müssen nicht einmal den tatsächlichen Inhalt vergleichen.

33
dtb

Es wird noch schneller, wenn Sie nicht kleine 8-Byte-Blöcke einlesen, sondern eine Schleife machen und einen größeren 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;
                }
            }
        }
    }
}
15
Lars

Das einzige, was einen Vergleich von Prüfsummen etwas schneller machen könnte als ein Vergleich von Byte für Byte, ist die Tatsache, dass Sie jeweils eine Datei lesen, wodurch die Suchzeit für den Plattenkopf etwas verringert wird. Dieser leichte Gewinn kann jedoch sehr gut durch die zusätzliche Zeit der Hashberechnung aufgezehrt werden.

Ein Checksummenvergleich von Kursen hat natürlich nur eine Chance, schneller zu sein, wenn die Dateien identisch sind. Wenn dies nicht der Fall ist, endet ein Byte-für-Byte-Vergleich mit dem ersten Unterschied, wodurch er wesentlich schneller wird.

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

Wenn der Hashcode zum Beispiel 32 Bit ist, sind Sie zu ungefähr 99,99999998% sicher, dass die Dateien identisch sind, wenn die Hashcodes übereinstimmen. Das ist fast 100%, aber wenn Sie wirklich hundertprozentige Sicherheit brauchen, ist es das nicht.

15
Guffa

Edit: Diese Methode würde nicht zum Vergleich von Binärdateien verwenden!

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

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

Was bedeutet, dass Sie verwenden könnten:

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

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

Dinge, die Sie überprüfen sollten, bevor Sie Byte für Byte weitergehen:

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

Das Lesen von großen Blöcken gleichzeitig ist effizienter, da Laufwerke sequenzielle Bytes schneller lesen. Das byteweise Umschalten führt nicht nur zu mehr Systemaufrufen, sondern auch dazu, dass der Lesekopf einer herkömmlichen Festplatte häufiger hin und her sucht, wenn sich beide Dateien auf demselben Laufwerk befinden.

Lesen Sie die Blöcke A und B in einen Byte-Puffer und vergleichen Sie sie (verwenden Sie KEINE Array.Equals, siehe Kommentare). Stellen Sie die Größe der Blöcke so ein, dass Sie einen guten Kompromiss zwischen Gedächtnis und Leistung erzielen. Sie können den Vergleich auch mit mehreren Threads durchführen, nicht jedoch mit dem Lesen der Festplatte.

6
RandomInsano

Meine Antwort ist eine Ableitung von @lars, behebt jedoch den Fehler im Aufruf von Stream.Read. Ich füge auch einige schnelle Pfadüberprüfungen hinzu, die andere Antworten hatten, und die Überprüfung der Eingaben. Kurz gesagt sollte dies 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 sein wollen, können Sie die async-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<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;
                    }
                }
            }
        }
    }
}
2
Andrew Arnott

Meine Experimente zeigen, dass es definitiv hilfreich ist, Stream.ReadByte () seltener aufzurufen, aber die Verwendung von BitConverter zum Packen von Bytes macht keinen großen Unterschied, wenn Sie Byte in einem Byte-Array vergleichen.

So ist es 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 mit der Tatsache zu tun, dass BitConverter.ToInt64 ein wenig Arbeit erledigen muss (Argumente prüfen und dann die Bitverschiebung durchführen), bevor Sie vergleichen, und das ist die gleiche Menge Arbeit wie beim Vergleich von 8 Byte in zwei Arrays .

2
romeok

Wenn Sie nur zwei Dateien vergleichen müssen, ist dies vermutlich der schnellste Weg (in C weiß ich nicht, ob dies für .NET gilt).

  1. Öffnen Sie beide Dateien f1, f2
  2. holen Sie sich die jeweilige Dateilänge l1, l2
  3. wenn l1! = l2 sind die Dateien unterschiedlich; halt
  4. mmap () beide Dateien
  5. verwenden Sie memcmp () für die mmap () - ed-Dateien

OTOH, wenn Sie feststellen müssen, ob doppelte Dateien in einer Gruppe von N-Dateien vorhanden sind, ist der schnellste Weg zweifellos die Verwendung eines Hashes, um bitweise Vergleiche in N-Richtung zu vermeiden.

1
CAFxX

Eine weitere Verbesserung bei großen Dateien mit identischer Länge könnte darin bestehen, die Dateien nicht sequenziell zu lesen, sondern mehr oder weniger zufällige Blöcke zu vergleichen.

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

Auf diese Weise können Sie Änderungen in der Mitte/am Ende der Datei schneller erkennen, als dies bei einem sequentiellen Ansatz der Fall wäre.

1
Thomas Kjørnes

Mit Hilfe einiger Hilfsfunktionen können Sie feststellen, ob zwei Dateien (oder zwei Streams) identische Daten enthalten.

Ich habe eine "schnelle" Version bereitgestellt, die Multithreading ist, da sie Byte-Arrays (jeder Puffer, der von dem gefüllt wird, was in jeder Datei gelesen wurde) in verschiedenen Threads mit Tasks vergleicht.

Wie erwartet, ist es viel schneller (etwa dreimal schneller), verbraucht jedoch mehr CPU (weil es Multi-Threading ist) und mehr Speicher (da zwei Byte-Array-Puffer pro Vergleichsthread benötigt werden).

    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;
    }
1
Simon Mourier

Etwas (hoffentlich) einigermaßen effizient:

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;
    }
}
1
Zar Shardan

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

Ein Vergleich von Hashes ist nur möglich, wenn die Hashwerte zum Speichern nützlich sind.

(Bearbeitet den Code zu etwas viel sauberer.)

1

Ich denke, es gibt Anwendungen, bei denen "Hash" schneller ist als ein Vergleich von Byte für Byte. Wenn Sie eine Datei mit anderen vergleichen müssen oder ein Miniaturbild eines Fotos haben, das geändert werden kann, hängt es davon ab, wo und wie es ist benutzt.

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 bekommen, 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 Datenbank speichern.

Hoffe das kann helfen 

0
antonio

Noch eine Antwort, abgeleitet von @chsh. MD5 mit Usings und Verknüpfungen für die gleiche Datei, Datei existiert nicht und unterschiedliche Längen

/// <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;
    }
}
0
Andrew Taylor