如何在Python中计算文件的MD5校验和?

162

我在Python中编写了一些代码,用于检查文件中的MD5哈希值,并确保哈希值与原始哈希值匹配。

这是我编写的代码:

# Defines filename
filename = "file.exe"

# Gets MD5 from file 
def getmd5(filename):
    return m.hexdigest()

md5 = dict()

for fname in filename:
    md5[fname] = getmd5(fname)

# If statement for alerting the user whether the checksum passed or failed

if md5 == '>md5 will go here<': 
    print("MD5 Checksum passed. You may now close this window")
    input ("press enter")
else:
    print("MD5 Checksum failed. Incorrect MD5 in file 'filename'. Please download a new copy")
    input("press enter") 
exit

但是每当我运行代码时,我会收到以下错误:

Traceback (most recent call last):
File "C:\Users\Username\md5check.py", line 13, in <module>
 md5[fname] = getmd5(fname)
File "C:\Users\Username\md5check.py, line 9, in getmd5
  return m.hexdigest()
NameError: global name 'm' is not defined

我的代码是否有任何遗漏之处?

4个回答

331
关于您的错误和代码中缺失的部分。 m 是一个未定义的名称,不适用于 getmd5() 函数。
不冒犯,我知道您是初学者,但是您的代码到处都是。 让我们一一看看您的问题 :)
首先,您没有正确使用 hashlib.md5.hexdigest() 方法。 请参考 hashlib 函数在 Python Doc Library 中的说明。 返回提供的 字符串 的 MD5 的正确方法是像这样做:
>>> import hashlib
>>> hashlib.md5("example string").hexdigest()
'2a53375ff139d9837e93a38a279d63e5'

然而,你在这里面临一个更大的问题。你正在计算文件名字符串的MD5值,而实际上MD5是基于文件内容计算的。你需要读取文件内容并将其传输到MD5中。我的下一个示例不是非常高效,但类似于以下内容:

>>> import hashlib
>>> hashlib.md5(open('filename.exe','rb').read()).hexdigest()
'd41d8cd98f00b204e9800998ecf8427e'

正如您可以清楚地看到的那样,第二个MD5哈希与第一个完全不同。原因是我们将文件的内容推送了进去,而不仅仅是文件名。

一个简单的解决方案可能是这样的:

# Import hashlib library (md5 method is part of it)
import hashlib

# File to check
file_name = 'filename.exe'

# Correct original md5 goes here
original_md5 = '5d41402abc4b2a76b9719d911017c592'  

# Open,close, read file and calculate MD5 on its contents 
with open(file_name, 'rb') as file_to_check:
    # read contents of the file
    data = file_to_check.read()    
    # pipe contents of the file through
    md5_returned = hashlib.md5(data).hexdigest()

# Finally compare original MD5 with freshly calculated
if original_md5 == md5_returned:
    print "MD5 verified."
else:
    print "MD5 verification failed!."

请查看文章Python:生成文件的MD5校验和,其中详细解释了几种高效实现方法。祝你好运。

2
哇,我感到非常尴尬。我猜我在做的事情上放错了代码,并且还加入了很多错误。谢谢你的帮助。虽然我更习惯批处理和Lua,所以对于Python来说有些挑剔。 - user2344996
34
你还应该使用二进制模式打开文件,代码为open(file_name, 'rb'),否则当操作系统进行换行符转换时,可能会遇到问题。请参阅 https://mail.python.org/pipermail/tutor/2004-January/027634.html 和 https://dev59.com/13A75IYBdhLWcg3wK10p?rq=1 - twobeers
5
如果您正在处理二进制文件,请确保使用“b”模式正确读取它。最后,我使用以下代码使其按预期工作:hashlib.sha512(open(fn, 'rb').read()).hexdigest() - Jammy Lee
生成较大文件的MD5哈希值需要不同的方法。您需要创建块并为每个块更新哈希值。参考 - Lokesh Sinha

66

在 Python 3.8+ 中,你可以做到

import hashlib

with open("your_filename.png", "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

在Python 3.7及以下版本中:

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

print(file_hash.hexdigest())

使用 f.read() 一次性读取文件会消耗大量内存,建议改为每次读取8192(或2¹³)字节的方式来读取文件。


考虑使用hashlib.blake2b替代md5(在上述片段中将md5替换为blake2b)。它具有加密安全性,并且比MD5更快


3
谢谢!在测试大约4MB的文件时,这种方法比内联“hashlib.md5(open('filename.exe','rb').read()).hexdigest()”始终快11%。 - Ryan Loggerythm
2
@RyanLoggerythm,这很令人惊讶,你确定你正在正确地进行分析吗?如果您有足够的RAM一次性将整个文件读入其中,我本来会认为这样会更快。此外,在2021年(甚至在2005年),4MB并不是“相当大”的文件。我们加载比这还要大的网站只是为了查看天气。 - user3064538
6
嘿,鲍里斯,是的,我正在使用32GB的RAM。当然,这只是一些快速测试,我的文件夹中最大的文件只有4MB :)。我刚刚生成了一个1GB的文件,PSS的单行哈希在5次运行中平均为1.695秒,而你的答案平均为1.454秒,时间减少了14%! - Ryan Loggerythm

8

hashlib 方法也支持 mmap 模块,所以我经常使用它们。

from hashlib import md5
from mmap import mmap, ACCESS_READ

path = ...
with open(path) as file, mmap(file.fileno(), 0, access=ACCESS_READ) as file:
    print(md5(file).hexdigest())

其中path是您文件的路径。

参考文献:https://docs.python.org/library/mmap.html#mmap.mmap

编辑:与普通读取方法的比较。

时间和内存使用情况的图表

from hashlib import md5
from mmap import ACCESS_READ, mmap

from matplotlib.pyplot import grid, legend, plot, show, tight_layout, xlabel, ylabel
from memory_profiler import memory_usage
from numpy import arange

def MemoryMap():
    with open(path) as file, mmap(file.fileno(), 0, access=ACCESS_READ) as file:
        print(md5(file).hexdigest())

def PlainRead():
    with open(path, 'rb') as file:
        print(md5(file.read()).hexdigest())

if __name__ == '__main__':
    path = ...
    y = memory_usage(MemoryMap, interval=0.01)
    plot(arange(len(y)) / 100, y, label='mmap')
    y = memory_usage(PlainRead, interval=0.01)
    plot(arange(len(y)) / 100, y, label='read')
    ylabel('Memory Usage (MiB)')
    xlabel('Time (s)')
    legend()
    grid()
    tight_layout()
    show()

path 是指指向一个大小为 3.77GB 的 CSV 文件的路径。


这会将整个文件读入内存吗?那为什么不直接使用 hashlib.md5(file.read()).hexdigest() 呢? - user3064538
@Boris 从图中看来,我认为mmap方法交替读取和处理,但似乎使用后内存没有被释放。 - liurui39660
非常敬重liurui39660所做的工作!我看到这个并认为两个impls最终使用相同的内存量,因此执行时间的差异将更加显著,具体取决于您获取单个文件的哈希值还是一亿个文件的哈希值。 - Michael Geiser
mmp的唯一问题是空文件。这会打破顺序并迫使代码在空文件情况下采用替代方法来获取MD5。 - DPalharini

-2

你可以通过读取二进制数据并使用 hashlib.md5().hexdigest() 来计算文件的校验和。一个实现这个功能的函数可能如下所示:

def File_Checksum_Dis(dirname):
    
    if not os.path.exists(dirname):
        print(dirname+" directory is not existing");
    
    for fname in os.listdir(dirname):
        if not fname.endswith('~'):
            fnaav = os.path.join(dirname, fname);
            fd = open(fnaav, 'rb');
            data = fd.read();
            fd.close();
        
            print("-"*70);
            print("File Name is: ",fname);          
            print(hashlib.md5(data).hexdigest())
            print("-"*70);
                

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接