25

使用 python 2.7,以下代码计算文件内容的 mD5 hexdigest。

(编辑:嗯,不是真的如答案所示,我只是这么认为)。

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()

现在,如果我使用 python3 运行该代码,它会引发 TypeError 异常:

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

我发现我可以让 python2 和 python3 都运行该代码,将其更改为:

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)。这种行为在某处有解释吗?

4

3 回答 3

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'))
于 2011-10-19T23:59:49.970 回答
10
for buf in f.read(128):
  d.update(buf)

.. 使用文件的前 128个字节值中的每一个顺序更新哈希值。由于迭代 abytes产生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()
于 2011-10-19T23:42:45.883 回答
1

在提出问题后,我终于将代码更改为以下版本(我觉得很容易理解)。但我可能会将其更改为 Raymond Hetting unsing 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()
于 2011-10-20T00:20:32.553 回答