14

我正在开发一个在整个驱动器中搜索给定文件的程序。目前,我计算已知文件的 MD5 哈希值,然后递归扫描所有文件,寻找匹配项。

唯一的问题是 MD5 在处理大文件时速度非常慢。是否有更快的替代方案可以使用,同时保留非常小的误报概率?

所有代码都在 C# 中。

谢谢你。

更新

我读过甚至 MD5 也可以很快,磁盘 I/O 应该是限制因素。这让我相信我的代码可能不是最优的。这种方法有什么问题吗?

        MD5 md5 = MD5.Create();
        StringBuilder sb = new StringBuilder();
        try
        {
            using (FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.Read))
            {
                foreach (byte b in md5.ComputeHash(fs))
                    sb.Append(b.ToString("X2"));
            }
            return sb.ToString();
        }
        catch (Exception)
        {
            return "";
        }
4

6 回答 6

48

我希望您仅在文件大小已经匹配时才检查 MD5 匹配。

另一个优化是对前 1K(或其他任意但相当小的数字)进行快速校验和,并在处理整个文件之前确保它们匹配。

当然,所有这些都假设您只是在寻找特定文件的匹配/不匹配决定。

于 2008-11-13T23:32:47.447 回答
10

无论加密要求如何,都存在哈希冲突的可能性,因此无法使用哈希函数来保证两个文件相同。

不久前我写了类似的代码,通过首先索引所有文件,然后丢弃任何不同大小的文件,我运行得非常快。然后对剩余的条目执行快速哈希比较(在每个文件的一部分上)(在此步骤中比较字节被证明不太有用 - 许多文件类型具有在文件开头具有相同字节的公共标头)。然后使用 MD5 检查在此阶段之后留下的所有文件,最后如果 MD5 匹配,则对整个文件进行字节比较,以确保内容相同。

于 2010-07-22T22:58:08.577 回答
6

只是线性读取文件?读取整个文件,计算 md5 哈希,然后比较哈希似乎毫无意义。

按顺序读取文件,一次几个字节,可以让您在读取 ​​4 个字节后丢弃绝大多数文件。而且您将节省计算散列函数的所有处理开销,这在您的情况下不会给您任何东西。

如果您已经拥有驱动器中所有文件的哈希值,那么比较它们是有意义的,但如果您必须即时计算它们,那么哈希值似乎没有任何优势。

我在这里错过了什么吗?在这种情况下,哈希会给你带来什么?

于 2008-11-13T23:31:04.687 回答
6

首先考虑一下真正的瓶颈是什么:哈希函数本身还是磁盘访问速度?如果您受到磁盘的限制,那么更改散列算法不会给您带来太多好处。根据您的描述,我暗示您总是在扫描整个磁盘以找到匹配项 - 考虑先构建索引,然后仅将给定的哈希与索引匹配,这会快得多。

于 2008-11-13T23:32:22.860 回答
5

使用 MD5 比较文件有一个小问题:已知有一对不同但具有相同MD5 的文件。

这意味着您可以使用 MD5 来判断文件是否不同(如果 MD5 不同,则文件必须不同),但不能使用 MD5 来判断文件是否相等(如果文件相等,则 MD5 必须是相同,但如果 MD5 相等,则文件可能相等也可能不相等)。

您应该使用尚未破坏的哈希函数(如 SHA-1),或者(如@SoapBox 提到的)仅使用 MD5 作为查找候选者进行更深入比较的快速方法。

参考:

于 2008-11-13T23:44:57.967 回答
0

使用 MD5CryptoServiceProvider 和 BufferedStream

        using (FileStream stream = File.OpenRead(filePath))
        {
            using (var bufferedStream = new BufferedStream(stream, 1024 * 32))
            {
                var sha = new MD5CryptoServiceProvider();
                byte[] checksum = sha.ComputeHash(bufferedStream);
                return BitConverter.ToString(checksum).Replace("-", String.Empty);
            }
        }
于 2013-07-19T10:30:22.777 回答