有没有简单的方法来生成(和检查)Python中文件列表的MD5校验和?(我有一个小程序,我正在研究,我想确认文件的校验和).
你可以使用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()
,因此您无需转换回来.
有一种方式是非常低效的内存.
单个文件:
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中发现的最新缺陷,可能甚至不是这样.有些人认为,只要你没有使用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(...)
.
我显然没有添加任何根本新的东西,但在我评论状态之前添加了这个答案,加上代码区域使事情变得更清楚 - 无论如何,专门回答来自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实现更快......
编辑:很抱歉长时间的延迟,有一段时间没有看过这个,但回答@ EdRandall的问题,我会写下Adler32的实现.但是,我没有为它运行基准测试.它与CRC32基本相同:不是init,update和digest调用,一切都是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
请注意,这必须从空字符串开始,因为当从零开始与其总和相比时,阿德勒总和确实不同""
,这是1
- CRC可以从而开始0
.AND
需要使用-ing使其成为32位无符号整数,这可确保它在Python版本中返回相同的值.