413

是否有任何简单的方法可以在 Python 中生成(和检查)文件列表的 MD5 校验和?(我有一个正在开发的小程序,我想确认文件的校验和)。

4

6 回答 6

590

您可以使用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(),因此您不必转换回来。

于 2010-08-07T19:53:52.250 回答
322

有一种内存效率很低的方法。

单个文件:

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,并且鉴于最近在 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(...)

于 2010-08-07T19:53:25.673 回答
35

我显然没有添加任何根本上的新内容,而是在我达到评论状态之前添加了这个答案,加上代码区域使事情变得更加清晰 - 无论如何,专门从 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

因此,对于一个 11MB 的文件,看起来 Python 和 /usr/bin/md5sum 都需要大约 30 毫秒。相关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

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

于 2014-02-04T23:45:06.063 回答
22

在 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(只需在上面的代码段中替换md5blake2b)。它比 MD5加密安全且速度更快。

于 2019-11-26T17:53:21.837 回答
14
hashlib.md5(pathlib.Path('path/to/file').read_bytes()).hexdigest()
于 2019-04-24T13:43:14.523 回答
-2

更改file_path为您的文件

import hashlib
def getMd5(file_path):
    m = hashlib.md5()
    with open(file_path,'rb') as f:
        line = f.read()
        m.update(line)
    md5code = m.hexdigest()
    return md5code
于 2021-02-19T07:26:24.653 回答