生成文件的MD5校验和

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

425042 次浏览

有一种方法是漂亮的内存效率低下的

单文件:

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中最近发现的缺陷,可能连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(...)的调用之后。

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

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

我碰巧想到了一些校验和(来这里寻找关于块大小的建议),并发现这种方法可能比您预期的要快。取最快的(但非常典型的)timeit.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(pathlib.Path('path/to/file').read_bytes()).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更

改变file_path到你的文件

import hashlib
def getMd5(file_path):
m = hashlib.md5()
with open(file_path,'rb') as f:
lines = f.read()
m.update(lines)
md5code = m.hexdigest()
return md5code

你可以使用simple-file-checksum1,它只使用subprocess来调用macOS/Linux的openssl和Windows的CertUtil,并且只从输出中提取摘要:

安装:

pip install simple-file-checksum

用法:

>>> from simple_file_checksum import get_checksum
>>> get_checksum("path/to/file.txt")
'9e107d9d372bb6826bd81d3542a419d6'
>>> get_checksum("path/to/file.txt", algorithm="MD5")
'9e107d9d372bb6826bd81d3542a419d6'

还支持SHA1SHA256SHA384SHA512算法。


1披露:我是simple-file-checksum的作者。

你可以利用这个壳层。

from subprocess import check_output


#for windows & linux
hash = check_output(args='md5sum imp_file.txt', shell=True).decode().split(' ')[0]


#for mac
hash = check_output(args='md5 imp_file.txt', shell=True).decode().split('=')[1]

在Python 3.11 +中,有新的可读和内存效率的方法:

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