31

我想比较两个二进制文件。其中一个已经存储在服务器上,并且从我最初存储它时开始在数据库中预先计算出 CRC32。

我知道如果CRC不同,那么文件肯定是不同的。但是,如果 CRC 相同,我不知道文件是否相同。因此,我正在寻找一种比较两个流的有效方法:一个来自发布的文件,一个来自文件系统。

我不是流方面的专家,但我很清楚,就内存使用而言,我可以很容易地在这里开枪打死自己。

4

6 回答 6

43
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;
    }
}
于 2009-06-09T09:05:21.310 回答
22

我通过在读取流块的循环中使用 Int64 比较来加速“memcmp”。这将时间减少到大约 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;
                }
            }
        }
    }
于 2010-04-14T12:30:39.713 回答
9

如果您不想依赖 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;
    }
于 2013-08-23T14:27:56.273 回答
4

接受的答案有一个错误被指出,但从未更正:流读取调用不能保证返回所有请求的字节。

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;
            }
        }
    }
于 2017-11-11T16:05:38.970 回答
3

如果您将该 crc 更改为 sha1 签名,那么它不同但具有相同签名的可能性非常小

于 2009-06-09T08:58:25.223 回答
2

您甚至可以在检查 CRC 之前检查两个文件的长度和日期,以可能避免 CRC 检查。

但是如果你必须比较整个文件的内容,我见过的一个巧妙的技巧是读取字节的步幅等于 CPU 的位数。例如,在 32 位 PC 上,一次读取 4 个字节并将它们作为 int32 进行比较。在 64 位 PC 上,您一次可以读取 8 个字节。这大约是逐字节执行速度的 4 或 8 倍。您可能还想使用一个不安全的代码块,以便您可以使用指针而不是进行一堆位移和 OR'ing 来将字节变成本机 int 大小。

您可以使用 IntPtr.Size 来确定当前处理器架构的理想大小。

于 2009-06-09T09:04:14.590 回答