有没有什么简单的方法可以在Python中生成(并检查)文件列表的MD5校验和?(我有一个小程序,我正在工作,我想确认文件的校验和)。


当前回答

在Python 3.8+中,你可以像这样使用赋值操作符:=(以及hashlib):

import hashlib
with open("your_filename.txt", "rb") as f:
    file_hash = hashlib.md5()
    while chunk := f.read(8192):
        file_hash.update(chunk)

print(file_hash.digest())
print(file_hash.hexdigest())  # to get a printable str instead of bytes

考虑使用hashlib。Blake2b而不是md5(在上面的代码片段中用Blake2b替换md5)。它是加密安全的,比MD5更快。

其他回答

hashlib.md5(pathlib.Path('path/to/file').read_bytes()).hexdigest()

我显然没有添加任何根本性的新内容,但在我达到评论状态之前添加了这个答案,加上代码区域使事情更加清晰——无论如何,特别回答@Nemo的问题来自Omnifarious的回答:

我碰巧想到了一些校验和(来这里寻找关于块大小的建议),并发现这种方法可能比您预期的要快。用最快的时间(但非常典型)。Timeit或/usr/bin/time是对一个约为。11 mb:

$ ./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和摘要调用,一切都是一个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和从0开始与“”的和(1)确实不同——CRC可以从0开始。需要AND-ing使其成为32位无符号整数,以确保它在不同Python版本中返回相同的值。

您可以使用hashlib.md5()

请注意,有时您无法将整个文件放入内存。在这种情况下,你必须依次读取4096字节的块,并将它们提供给md5方法:

import hashlib
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(),这样你就不必转换回来。

在Python 3.11+中,有一个新的可读且内存高效的方法:

import hashlib
with open(path, "rb") as f:
    digest = hashlib.file_digest(f, "md5")
print(digest.hexdigest())

在Python 3.8+中,你可以像这样使用赋值操作符:=(以及hashlib):

import hashlib
with open("your_filename.txt", "rb") as f:
    file_hash = hashlib.md5()
    while chunk := f.read(8192):
        file_hash.update(chunk)

print(file_hash.digest())
print(file_hash.hexdigest())  # to get a printable str instead of bytes

考虑使用hashlib。Blake2b而不是md5(在上面的代码片段中用Blake2b替换md5)。它是加密安全的,比MD5更快。