有没有简单的方法来生成(和检查)Python中文件列表的MD5校验和? (我有一个小程序,我正在研究,我想确认文件的校验和)。
答案 0 :(得分:359)
您可以使用hashlib.md5()
请注意,有时您无法将整个文件放入内存中。在这种情况下,您必须按顺序读取4096字节的块并将它们提供给Md5函数:
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()
打包字节,那么您不必转回来。
答案 1 :(得分:283)
有一种方式是非常好的记忆效率低下。
单个文件:
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 is known broken并且不应该用于任何目的,因为漏洞分析可能非常棘手,并且分析任何可能的未来使用,您的代码可能会被用于安全问题是不可能的。恕我直言,它应该是从图书馆中删除的,所以每个使用它的人都被迫更新。所以,这是你应该做的事情:
[(fname, hashlib.sha256(file_as_bytes(open(fname, 'rb'))).digest()) for fname in fnamelst]
如果您只想要128位摘要,可以.digest()[:16]
。
这将为您提供元组列表,每个元组包含其文件名及其哈希值。
我再次强烈质疑您对MD5的使用。您应该至少使用SHA1,并且给定recent flaws discovered in 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(...)
。
答案 2 :(得分:30)
我显然没有添加任何根本新的内容,但在我评论状态之前添加了这个答案,加上代码区域使事情变得更清楚 - 无论如何,专门回答来自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
因此,看起来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实现更快......
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 sum确实有所不同,而""
的总和为1
- CRC可以从{{1}开始相反。需要0
- 才能使其成为32位无符号整数,这可确保它在Python版本中返回相同的值。
答案 3 :(得分:0)
我认为依靠invoke包和md5sum二进制文件比子进程或md5包更方便
import invoke
def get_file_hash(path):
return invoke.Context().run("md5sum {}".format(path), hide=True).stdout.split(" ")[0]
这当然假定您已经安装了invoke和md5sum。
答案 4 :(得分:0)
在Python 3.8+中,您可以做到
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
(只需将以上代码段中的md5
替换为blake2b
)。与MD5相比,它是加密安全的,faster。
答案 5 :(得分:-1)
将 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
答案 6 :(得分:-2)
hashlib.md5(pathlib.Path('path/to/file').read_bytes()).hexdigest()