生成文件的MD5校验和

问题描述 投票:272回答:4

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

python md5 checksum hashlib
4个回答
367
投票

你可以使用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()

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


284
投票

有一种方式是非常低效的内存。

单个文件:

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 is known broken并不应该用于任何目的,因为漏洞分析可能非常棘手,并且分析任何可能的未来使用,您的代码可能会被用于安全问题是不可能的。恕我直言,它应该是从图书馆中删除的,所以每个使用它的人都被迫更新。所以,这是你应该做的事情:

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

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

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

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

这是一种更复杂但内存效率更高的方法:

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位的摘要,你可以在调用[:16]之后放入hash_bytestr_iter(...)


31
投票

我显然没有添加任何根本新的东西,但在我评论状态之前添加了这个答案,加上代码区域使事情变得更清楚 - 无论如何,专门回答来自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实现更快......

编辑:很抱歉长时间的延迟,有一段时间没有看过这个,但回答@ EdRandall的问题,我会写下一个Adler32实现。但是,我没有为它运行基准测试。它基本上与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

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


-2
投票
hashlib.md5(pathlib.Path('path/to/file').read_bytes()).hexdigest()
© www.soinside.com 2019 - 2024. All rights reserved.