首页 文章

生成文件的MD5校验和

提问于
浏览
241

有没有简单的方法来生成(和检查)Python中文件列表的MD5校验和? (我有一个小程序,我正在研究,我想确认文件的校验和) .

3 回答

  • 275

    有一种方式是相当美好的记忆 inefficient .

    单个文件:

    import hashlib
    def file_as_bytes(file):
        with file:
            return file.read()
    
    print hashlib.md5(file_as_bytes(open(full_path, 'rb'))).hexdigest()
    

    文件列表:

    [(fname, hashlib.md5(file_as_bytes(open(fname, 'rb'))).digest()) for fname in fnamelst]
    

    但是,MD5已知故障并且(恕我直言)应该带有可怕的弃用警告并从库中删除,所以这是你应该如何实际做到的:

    [(fname, hashlib.sha256(file_as_bytes(open(fname, 'rb'))).digest()) for fname in fnamelst]
    

    如果你只想要128位的摘要,你可以做 .digest()[:16] .

    这将为您提供元组列表,每个元组包含其文件名及其哈希值 .

    我再次强烈质疑你对MD5的使用 . 你应该至少使用SHA1 . 有些人认为,只要你不使用MD5进行“加密”目的,你就可以了 . 但是,事物的范围往往比你最初预期的范围更广,你的随意漏洞分析可能证明是完全有缺陷的 . 最好养成在门外使用正确算法的习惯 . 它只是输入一堆不同的字母 . 这并不难 .

    这是一种更复杂的方式,但 memory efficient

    import hashlib
    
    def hash_bytestr_iter(bytesiter, hasher, ashexstr=False):
        for block in bytesiter:
            hasher.update(block)
        return (hasher.hexdigest() if ashexstr else hasher.digest())
    
    def file_as_blockiter(afile, blocksize=65536):
        with afile:
            block = afile.read(blocksize)
            while len(block) > 0:
                yield block
                block = afile.read(blocksize)
    
    
    [(fname, hash_bytestr_iter(file_as_blockiter(open(fname, 'rb')), hashlib.md5()))
        for fname in fnamelst]
    

    而且,再次,因为MD5已经崩溃,不应该再被使用了:

    [(fname, hash_bytestr_iter(file_as_blockiter(open(fname, 'rb')), hashlib.sha256()))
        for fname in fnamelst]
    

    同样,如果你只需要128位的摘要,你可以在调用 hash_bytestr_iter(...) 之后输入 [:16] .

  • 29

    我显然没有添加任何根本新的东西,但在我评论状态之前添加了这个答案,加上代码区域使事情变得更清楚 - 无论如何,特别是从Omnifarious的回答中回答@ Nemo的问题:

    我碰巧考虑了一些校验和(来这里寻找有关块大小的建议,特别是),并且发现这种方法可能比你预期的要快 . 从几种校验和大约文件的方法中获取最快(但非常典型)的 timeit.timeit/usr/bin/time 结果 . 11MB:

    $ ./sum_methods.py
    crc32_mmap(filename) 0.0241742134094
    crc32_read(filename) 0.0219960212708
    subprocess.check_output(['cksum', filename]) 0.0553209781647
    md5sum_mmap(filename) 0.0286180973053
    md5sum_read(filename) 0.0311000347137
    subprocess.check_output(['md5sum', filename]) 0.0332629680634
    $ time md5sum /tmp/test.data.300k
    d3fe3d5d4c2460b5daacc30c6efbc77f  /tmp/test.data.300k
    
    real    0m0.043s
    user    0m0.032s
    sys     0m0.010s
    $ stat -c '%s' /tmp/test.data.300k
    11890400
    

    因此,看起来Python和/ usr / bin / md5sum对于11MB文件大约需要30ms . 相关的 md5sum 函数(上面列表中的 md5sum_read )与Omnifarious的非常类似:

    import hashlib
    def md5sum(filename, blocksize=65536):
        hash = hashlib.md5()
        with open(filename, "rb") as f:
            for block in iter(lambda: f.read(blocksize), b""):
                hash.update(block)
        return hash.hexdigest()
    

    当然,这些来自单次运行(当至少进行几十次运行时, mmap 总是更快一点),并且在缓冲区耗尽后我的通常会得到一个额外的 f.read(blocksize) ,但它是合理可重复的并且显示 md5sum 命令行不一定比Python实现更快......

    编辑:很抱歉长时间的延迟,避免't looked at this in some time, but to answer @EdRandall'的问题,我'll write down an Adler32 implementation. However, I haven' t运行它的基准 . 它基本上与CRC32一样:而不是init,update和digest调用,一切都是 zlib.adler32() 调用:

    import zlib
    def adler32sum(filename, blocksize=65536):
        checksum = zlib.adler32("")
        with open(filename, "rb") as f:
            for block in iter(lambda: f.read(blocksize), b""):
                checksum = zlib.adler32(block, checksum)
        return checksum & 0xffffffff
    

    请注意,这必须以空字符串开头,因为当从零开始时,Adler sum确实不同,而 "" 的总和是 1 - CRC可以从 0 开始 . 需要 AND -ing使其成为32位无符号整数,这可确保它在Python版本中返回相同的值 .

  • 314

    你可以用hashlib.md5()

    请注意,有时您将无法将整个文件放入内存中 . 在这种情况下,您必须按顺序读取4096字节的块并将它们提供给Md5函数:

    def md5(fname):
        hash_md5 = hashlib.md5()
        with open(fname, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
        return hash_md5.hexdigest()
    

    Note: hash_md5.hexdigest() 将返回摘要的十六进制字符串表示,如果您只需要打包字节使用 return hash_md5.digest() ,那么您不必转换回来 .

相关问题