Подтвердить что ты не робот

Сравнение двоичных файлов в С#

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

Я ускорил "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;
                }
            }
        }
    }

Ответ 3

Вот как я это сделал бы, если бы вы не хотели полагаться на 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;
    }

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

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

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