在Python 3中使用hashlib计算文件的md5摘要

25 投票
3 回答
27339 浏览
提问于 2025-04-17 04:39

在Python 2.7中,下面的代码可以计算一个文件内容的MD5十六进制值。

(编辑:其实并不是这样,之前的回答让我意识到我理解错了)。

import hashlib

def md5sum(filename):
    f = open(filename, mode='rb')
    d = hashlib.md5()
    for buf in f.read(128):
        d.update(buf)
    return d.hexdigest()

但是当我用Python 3运行这段代码时,它会抛出一个类型错误的异常:

    d.update(buf)
TypeError: object supporting the buffer API required

我发现我可以通过修改代码,让它在Python 2和Python 3中都能运行:

def md5sum(filename):
    f = open(filename, mode='r')
    d = hashlib.md5()
    for buf in f.read(128):
        d.update(buf.encode())
    return d.hexdigest()

现在我还是想知道,为什么原来的代码不再工作了。看起来在用二进制模式打开文件时,它返回的是整数,而不是编码为字节的字符串(我这么说是因为type(buf)返回的是int)。这个行为有没有地方解释过?

3 个回答

2

我在问这个问题后,终于把我的代码改成了下面这个版本(我觉得这样更容易理解)。不过我可能会把它改成Raymond Hetting建议的版本,使用functools.partial。

import hashlib

def chunks(filename, chunksize):
    f = open(filename, mode='rb')
    buf = "Let's go"
    while len(buf):
        buf = f.read(chunksize)
        yield buf

def md5sum(filename):
    d = hashlib.md5()
    for buf in chunks(filename, 128):
        d.update(buf)
    return d.hexdigest()
10
for buf in f.read(128):
  d.update(buf)

.. 会依次用文件的前128个字节的值来更新哈希值。因为遍历一个bytes对象会产生int类型的对象,所以你会得到一些调用,这些调用导致了你在Python3中遇到的错误。

d.update(97)
d.update(98)
d.update(99)
d.update(100)

这并不是你想要的结果。

相反,你想要的是:

def md5sum(filename):
  with open(filename, mode='rb') as f:
    d = hashlib.md5()
    while True:
      buf = f.read(4096) # 128 is smaller than the typical filesystem block
      if not buf:
        break
      d.update(buf)
    return d.hexdigest()
37

我觉得你想让这个for循环连续调用一次又一次的 f.read(128)。你可以用 iter()functools.partial() 来实现这个功能:

import hashlib
from functools import partial

def md5sum(filename):
    with open(filename, mode='rb') as f:
        d = hashlib.md5()
        for buf in iter(partial(f.read, 128), b''):
            d.update(buf)
    return d.hexdigest()

print(md5sum('utils.py'))

撰写回答