Сравните двоичные файлы в C # - PullRequest
30 голосов
/ 09 июня 2009

Я хочу сравнить два двоичных файла. Один из них уже хранится на сервере с предварительно рассчитанным CRC32 в базе данных с момента его первоначального хранения.

Я знаю, что если CRC другой, то файлы определенно разные. Однако, если CRC такой же, я не знаю, что это за файлы. Итак, я ищу хороший эффективный способ сравнения двух потоков: одного из опубликованного файла и одного из файловой системы.

Я не специалист по потокам, но я хорошо знаю, что могу легко выстрелить себе в ногу, если говорить об использовании памяти.

Ответы [ 6 ]

43 голосов
/ 09 июня 2009
static bool FileEquals(string fileName1, string fileName2)
{
    // Check the file size and CRC equality here.. if they are equal...    
    using (var file1 = new FileStream(fileName1, FileMode.Open))
        using (var file2 = new FileStream(fileName2, FileMode.Open))
            return FileStreamEquals(file1, file2);
}

static bool FileStreamEquals(Stream stream1, Stream stream2)
{
    const int bufferSize = 2048;
    byte[] buffer1 = new byte[bufferSize]; //buffer size
    byte[] 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;

        // You might replace the following with an efficient "memcmp"
        if (!buffer1.Take(count1).SequenceEqual(buffer2.Take(count2)))
            return false;
    }
}
20 голосов
/ 14 апреля 2010

Я ускорил "memcmp", используя сравнение Int64 в цикле над кусками потока чтения. Это сократило время примерно до 1/4.

    private static bool StreamsContentsAreEqual(Stream stream1, Stream stream2)
    {
        const int bufferSize = 2048 * 2;
        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;
                }
            }
        }
    }
6 голосов
/ 23 августа 2013

Вот как бы я это сделал, если бы вы не хотели полагаться на crc:

    /// <summary>
    /// Binary comparison of two files
    /// </summary>
    /// <param name="fileName1">the file to compare</param>
    /// <param name="fileName2">the other file to compare</param>
    /// <returns>a value indicateing weather the file are identical</returns>
    public static bool CompareFiles(string fileName1, string fileName2)
    {
        FileInfo info1 = new FileInfo(fileName1);
        FileInfo info2 = new FileInfo(fileName2);
        bool same = info1.Length == info2.Length;
        if (same)
        {
            using (FileStream fs1 = info1.OpenRead())
            using (FileStream fs2 = info2.OpenRead())
            using (BufferedStream bs1 = new BufferedStream(fs1))
            using (BufferedStream bs2 = new BufferedStream(fs2))
            {
                for (long i = 0; i < info1.Length; i++)
                {
                    if (bs1.ReadByte() != bs2.ReadByte())
                    {
                        same = false;
                        break;
                    }
                }
            }
        }

        return same;
    }
3 голосов
/ 09 июня 2009

если вы измените этот crc на сигнатуру sha1, шансы того, что он будет другим, но с одной и той же сигнатурой, астрономически невелики

2 голосов
/ 11 ноября 2017

В принятом ответе была ошибка, которая была указана, но никогда не исправлялась: потоковые вызовы чтения не гарантированно возвращают все запрошенные байты.

BinaryReader ReadBytes вызовы гарантированно вернут столько байтов, сколько требуется, если конец потока не достигнут первым.

Для сравнения используется следующий код BinaryReader :

    static private bool FileEquals(string file1, string file2)
    {
        using (FileStream s1 = new FileStream(file1, FileMode.Open, FileAccess.Read, FileShare.Read))
        using (FileStream s2 = new FileStream(file2, FileMode.Open, FileAccess.Read, FileShare.Read))
        using (BinaryReader b1 = new BinaryReader(s1))
        using (BinaryReader b2 = new BinaryReader(s2))
        {
            while (true)
            {
                byte[] data1 = b1.ReadBytes(64 * 1024);
                byte[] data2 = b2.ReadBytes(64 * 1024);
                if (data1.Length != data2.Length)
                    return false;
                if (data1.Length == 0)
                    return true;
                if (!data1.SequenceEqual(data2))
                    return false;
            }
        }
    }
2 голосов
/ 09 июня 2009

Вы можете проверить длину и даты двух файлов даже перед проверкой CRC, чтобы, возможно, избежать проверки CRC.

Но если вам нужно сравнить все содержимое файла, я обнаружил одну хитрость: чтение байтов с шагом, равным разрядности процессора. Например, на 32-битном ПК читайте 4 байта за раз и сравнивайте их как int32. На 64-битном ПК вы можете читать 8 байтов за раз. Это примерно в 4 или 8 раз быстрее, чем делать это побайтово. Вы также, вероятно, захотите использовать небезопасный блок кода, чтобы вы могли использовать указатели вместо того, чтобы выполнять сдвиг битов и операции OR, чтобы получить байты в натуральных размерах int.

Вы можете использовать IntPtr.Size, чтобы определить идеальный размер для текущей архитектуры процессора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...