检查两个文件是否相等的最快哈希算法是什么?

时间:2009-11-19 07:52:55

标签: file hash crc

创建哈希函数的最快方法是什么,用于检查两个文件是否相等?

安全性不是很重要。

编辑:我通过网络连接发送文件,并确保双方的文件相同

12 个答案:

答案 0 :(得分:45)

除非您使用的是非常复杂和/或速度较慢的哈希,否则从磁盘加载数据所需的时间比计算哈希要长得多(除非您使用RAM磁盘或高端SSD)。

因此,要比较两个文件,请使用此算法:

  • 比较尺寸
  • 比较日期(这里要小心:这可能会给你错误的答案;你必须测试是否适合你)
  • 比较哈希

这允许快速失败(如果大小不同,您知道文件不同)。

为了使事情变得更快,您可以计算一次哈希并将其与文件一起保存。还要将文件日期和大小保存到这个额外的文件中,以便在主文件发生更改时必须重新计算哈希值或删除哈希文件时快速了解。

答案 1 :(得分:25)

一种方法可能是使用简单的CRC-32算法,并且只有当CRC值相等时,才能使用SHA1或更强大的东西重新运行散列。快速CRC-32将在任何一天都优于加密安全散列。

答案 2 :(得分:19)

xxhash声称自己非常快速和强大,碰撞明智:

http://cyan4973.github.io/xxHash/

有一个64位变体在64位处理器上运行“甚至更快”而不是32位整体,但在32位处理器上运行速度较慢(如图所示)。

http://code.google.com/p/crcutil也被称为相当快(并且利用硬件CRC指令,如果存在,可能非常快,但如果你没有支持它们的硬件,则不是那么快)。不知道CRC32c是否与xxHash一样好(就碰撞而言)...

https://code.google.com/p/cityhash/似乎与crcutil相似[因为它可以编译下来使用硬件CRC32c指令]。

如果您“只想要最快的原始速度”并且不关心散列输出的随机分布质量(例如,使用小集合,或者速度至关重要),则会提到一些快速算法这里:http://www.sanmayce.com/Fastest_Hash/(这些“不太随机”的分布式算法在某些情况下“足够好”且非常快)。显然FNV1A_Jesteress是“长”字符串最快的,有些可能是小字符串。 http://locklessinc.com/articles/fast_hash/似乎也很相关。我没有研究看这些碰撞属性是什么。

答案 3 :(得分:3)

你可以试试MurmurHash,这是专门设计得很快的,并且编码非常简单。你可能想要和第二个更安全的哈希,如果MurmurHash返回一个匹配,只是为了确定。

答案 4 :(得分:3)

对于这种类型的应用程序,Adler32可能是最快的算法,具有合理的安全级别。对于较大的文件,您可以计算多个散列值,例如每个块5 Mb的一个散列值,从而减少错误的机会(即,当散列相同但文件内容不同时的情况)。此外,这种多哈希值设置可以允许以多线程方式实现哈希的计算。

编辑 :(遵循Steven Sudit的评论)
如果文件很小,请注意!
Adler32的“加密”属性,或者更确切地说是它的弱点,尤其对于短消息而言是众所周知的。因此,对于小于几千字节的文件,应避免建议的解决方案 从来没有,在这个问题上,OP明确地寻求快速算法并且放弃对安全性的担忧。此外,对速度的追求可能合理地暗示正在处理“大”文件而不是小文件。在这种情况下,Adler32可能并行应用于5Mb的文件块仍然是一个非常有效的答案。 Alder32因其简单和速度而闻名。此外,它的可靠性虽然仍然低于相同长度的CRC,但对于4000字节以上的消息来说是完全可以接受的。

答案 5 :(得分:2)

如果它只有一个关闭,那么你必须阅读这两个文件来生成两者的哈希值,为什么不一次只读取少量文件并进行比较呢?

失败CRC是一个非常简单的算法。

答案 6 :(得分:2)

我们在这里优化的是花在任务上的时间。 不幸的是,我们对手头的任务知之甚少,不知道最佳解决方案应该是什么。

是否对2个任意文件进行一次性比较? 然后比较大小,然后简单地比较文件,逐字节(或mb乘mb),如果这对你的IO更好。

如果是2组大型文件或多组文件,则不是一次性练习。但是经常发生的事情,那么应该为每个文件存储哈希值。散列永远不会是唯一的,但是具有多个9位(32位)的散列对于大约40亿个组合是好的,并且64位数量足以区分一些16 * 10 ^ 18个Quintillion不同的文件

一个不错的折衷方案是为每个文件生成2个32位哈希值,一个用于前8k,另一个用于1MB + 8k,将它们作为单个64位数一起打击。将所有现有文件编目到数据库中应该相当快,并且针对此数据库查找候选文件也应该非常快。一旦匹配,确定它们是否相同的唯一方法是比较整个文件。

我相信能够为人们提供他们需要的东西,但这并不总是他们认为自己需要的东西,或者他们想要的东西。

答案 7 :(得分:1)

你为什么要哈希呢?

如果你想确保两个文件是相同的,那么按照定义你必须读取整个文件(除非它们确实是同一个文件,在这种情况下你可以通过查看文件系统上的元数据来判断)。无论如何,没有理由哈希,只是阅读它们,看看它们是否相同。哈希会降低效率。即使哈希匹配,你仍然不确定文件是否真的相同。

编辑:此答案是在问题指定网络之前发布的。它只询问了比较两个文件。现在我知道文件之间有网络跳跃,我想说只需使用MD5哈希并完成它。

答案 8 :(得分:1)

在任何情况下,您都应该完全读取每个文件(大小不匹配的情况除外),所以只需读取文件并比较块到块。

使用hash只会获得CPU使用率,仅此而已。由于你没有写任何东西,操作系统的缓存将有效地删除你读取的数据,因此,在Linux下,只需使用cmp tool

答案 9 :(得分:1)

以下是从我的个人项目中查找重复文件以对图片进行排序的代码,这些图片也会删除重复项。根据我的经验,首先使用快速散列算法,如CRC32,然后做MD5或SHA1甚至更慢,并没有做任何改进,因为大多数具有相同大小的文件确实重复,因此从CPU时间角度来看,运行散列两次更昂贵,这种方法对于所有类型的项目可能都不正确,但对于图像文件来说肯定是正确的。这里我只对相同大小的文件进行MD5或SHA1哈希处理。

PS:它依赖于Apache commons编解码器来有效地生成散列。

示例用法: new DuplicateFileFinder(“MD5”)。findDuplicateFilesList(filesList);

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;

    import org.apache.commons.codec.digest.DigestUtils;

    /**
     * Finds the duplicate files using md5/sha1 hashing, which is used only for the sizes which are of same size.
     *  
     * @author HemantSingh
     *
     */
    public class DuplicateFileFinder {

        private HashProvider hashProvider;
        // Used only for logging purpose.
        private String hashingAlgo;

        public DuplicateFileFinder(String hashingAlgo) {
            this.hashingAlgo = hashingAlgo;
            if ("SHA1".equalsIgnoreCase(hashingAlgo)) {
                hashProvider = new Sha1HashProvider();
            } else if ("MD5".equalsIgnoreCase(hashingAlgo)) {
                hashProvider = new Md5HashProvider();
            } else {
                throw new RuntimeException("Unsupported hashing algorithm:" + hashingAlgo + " Please use either SHA1 or MD5.");
            }
        }

        /**
         * This API returns the list of duplicate files reference.
         * 
         * @param files
         *            - List of all the files which we need to check for duplicates.
         * @return It returns the list which contains list of duplicate files for
         *         e.g. if a file a.JPG have 3 copies then first element in the list
         *         will be list with three references of File reference.
         */
        public List<List<File>> findDuplicateFilesList(List<File> files) {
            // First create the map for the file size and file reference in the array list.
            Map<Long, List<File>> fileSizeMap = new HashMap<Long, List<File>>();
            List<Long> potDuplicateFilesSize = new ArrayList<Long>();

            for (Iterator<File> iterator = files.iterator(); iterator.hasNext();) {
                File file = (File) iterator.next();
                Long fileLength = new Long(file.length());
                List<File> filesOfSameLength = fileSizeMap.get(fileLength);
                if (filesOfSameLength == null) {
                    filesOfSameLength = new ArrayList<File>();
                    fileSizeMap.put(fileLength, filesOfSameLength);
                } else {
                    potDuplicateFilesSize.add(fileLength);
                }
                filesOfSameLength.add(file);
            }

            // If we don't have any potential duplicates then skip further processing.
            if (potDuplicateFilesSize.size() == 0) {
                return null;
            }

            System.out.println(potDuplicateFilesSize.size() + " files will go thru " + hashingAlgo + " hash check to verify if they are duplicate.");

            // Now we will scan the potential duplicate files, and eliminate false positives using md5 hash check.
            List<List<File>> finalListOfDuplicates = new ArrayList<List<File>>();
            for (Iterator<Long> potDuplicatesFileSizeIterator = potDuplicateFilesSize
                    .iterator(); potDuplicatesFileSizeIterator.hasNext();) {
                Long fileSize = (Long) potDuplicatesFileSizeIterator.next();
                List<File> potDupFiles = fileSizeMap.get(fileSize);
                Map<String, List<File>> trueDuplicateFiles = new HashMap<String, List<File>>();
                for (Iterator<File> potDuplicateFilesIterator = potDupFiles.iterator(); potDuplicateFilesIterator
                        .hasNext();) {
                    File file = (File) potDuplicateFilesIterator.next();
                    try {
                        String md5Hex = hashProvider.getHashHex(file);
                        List<File> listOfDuplicatesOfAFile = trueDuplicateFiles.get(md5Hex);
                        if (listOfDuplicatesOfAFile == null) {
                            listOfDuplicatesOfAFile = new ArrayList<File>();
                            trueDuplicateFiles.put(md5Hex, listOfDuplicatesOfAFile);
                        }
                        listOfDuplicatesOfAFile.add(file);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                Collection<List<File>> dupsOfSameSizeList = trueDuplicateFiles.values();
                for (Iterator<List<File>> dupsOfSameSizeListIterator = dupsOfSameSizeList.iterator(); dupsOfSameSizeListIterator
                        .hasNext();) {
                    List<File> list = (List<File>) dupsOfSameSizeListIterator.next();
                    // It will be duplicate only if we have more then one copy of it.
                    if (list.size() > 1) {
                        finalListOfDuplicates.add(list);
                        System.out.println("Duplicate sets found: " + finalListOfDuplicates.size());
                    }
                }
            }

            return finalListOfDuplicates;
        }

        abstract class HashProvider {
            abstract String getHashHex(File file) throws IOException ;
        }

        class Md5HashProvider extends HashProvider {
            String getHashHex(File file) throws IOException {
                return DigestUtils.md5Hex(new FileInputStream(file));
            }
        }
        class Sha1HashProvider extends HashProvider {
            String getHashHex(File file) throws IOException {
                return DigestUtils.sha1Hex(new FileInputStream(file));
            }
        }
    }

答案 10 :(得分:0)

您可以查看samba / rsync开发人员使用的算法。我没有深入研究它,但我看到它一直在提及。显然它非常好。

答案 11 :(得分:0)

我记得旧的调制解调器传输协议(如Zmodem)会在发送每个块时对其进行某种CRC比较。 CRC32,如果我还记得很久的历史。我不建议您制定自己的传输协议,除非正是您要执行的操作,但是您可以让它定期对文件的某个块进行抽查,或者对于每个8k块进行哈希处理对于处理器来处理。我还没有尝试过。