我使用了hashlib(在Python 2.6/3.0中取代了md5),如果我打开一个文件并将其内容放入
hashlib.md5()
函数中,它就可以正常工作。
问题在于文件非常大,其大小可能会超过 RAM 大小。
如何在不将整个文件加载到内存中的情况下获取文件的 MD5 哈希值?
您需要以合适大小的块读取文件:
def md5_for_file(f, block_size=2**20):
md5 = hashlib.md5()
while True:
data = f.read(block_size)
if not data:
break
md5.update(data)
return md5.digest()
注意:确保使用“rb”打开文件 - 否则你会得到错误的结果。
因此,要用一种方法完成所有工作 - 使用类似以下内容:
def generate_file_md5(rootdir, filename, blocksize=2**20):
m = hashlib.md5()
with open( os.path.join(rootdir, filename) , "rb" ) as f:
while True:
buf = f.read(blocksize)
if not buf:
break
m.update( buf )
return m.hexdigest()
上面的更新基于 Frerich Raabe 提供的评论 - 我对此进行了测试,发现它在我的 Python 2.7.2 Windows 安装上是正确的
我使用 jacksum 工具交叉检查了结果。
jacksum -a md5 <filename>
将文件分成 8192 字节的块(或 128 字节的其他倍数)并使用
update()
将它们连续输入 MD5。
这利用了 MD5 有 128 字节摘要块的事实(8192 是 128×64)。由于您没有将整个文件读入内存,因此不会使用超过 8192 字节的内存。
在 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
import hashlib
def checksum(filename, hash_factory=hashlib.md5, chunk_num_blocks=128):
h = hash_factory()
with open(filename,'rb') as f:
for chunk in iter(lambda: f.read(chunk_num_blocks*h.block_size), b''):
h.update(chunk)
return h.digest()
import hashlib
def checksum(filename, hash_factory=hashlib.md5, chunk_num_blocks=128):
h = hash_factory()
with open(filename,'rb') as f:
while chunk := f.read(chunk_num_blocks*h.block_size):
h.update(chunk)
return h.digest()
如果你想要一种更Pythonic(没有
while True
)的方式来读取文件,请检查以下代码:
import hashlib
def checksum_md5(filename):
md5 = hashlib.md5()
with open(filename,'rb') as f:
for chunk in iter(lambda: f.read(8192), b''):
md5.update(chunk)
return md5.digest()
请注意,
iter()
函数需要一个空字节字符串,以便返回的迭代器在 EOF 处停止,因为 read()
返回 b''
(不仅仅是 ''
)。
这是我的Piotr Czapla 方法版本:
def md5sum(filename):
md5 = hashlib.md5()
with open(filename, 'rb') as f:
for chunk in iter(lambda: f.read(128 * md5.block_size), b''):
md5.update(chunk)
return md5.hexdigest()
针对这个问题使用多个评论/答案,这是我的解决方案:
import hashlib
def md5_for_file(path, block_size=256*128, hr=False):
'''
Block size directly depends on the block size of your filesystem
to avoid performances issues
Here I have blocks of 4096 octets (Default NTFS)
'''
md5 = hashlib.md5()
with open(path,'rb') as f:
for chunk in iter(lambda: f.read(block_size), b''):
md5.update(chunk)
if hr:
return md5.hexdigest()
return md5.digest()
Python 2/3 可移植解决方案
要计算校验和(md5、sha1 等),您必须以二进制模式打开文件,因为您将对字节值求和:
要使 Python 2.7 和 Python 3 可移植,您应该使用
io
包,如下所示:
import hashlib
import io
def md5sum(src):
md5 = hashlib.md5()
with io.open(src, mode="rb") as fd:
content = fd.read()
md5.update(content)
return md5
如果您的文件很大,您可能更喜欢按块读取文件,以避免将整个文件内容存储在内存中:
def md5sum(src, length=io.DEFAULT_BUFFER_SIZE):
md5 = hashlib.md5()
with io.open(src, mode="rb") as fd:
for chunk in iter(lambda: fd.read(length), b''):
md5.update(chunk)
return md5
iter()
函数与 sentinel(空字符串)一起使用。
在这种情况下创建的迭代器将调用 o [lambda 函数],每次调用其
方法时不带任何参数;如果返回的值等于sentinel,则next()
将被提升,否则将返回该值。StopIteration
如果您的文件真的很大,您可能还需要显示进度信息。您可以通过调用打印或记录计算的字节数的回调函数来做到这一点:
def md5sum(src, callback, length=io.DEFAULT_BUFFER_SIZE):
calculated = 0
md5 = hashlib.md5()
with io.open(src, mode="rb") as fd:
for chunk in iter(lambda: fd.read(length), b''):
md5.update(chunk)
calculated += len(chunk)
callback(calculated)
return md5
Bastien Semene 的代码的混音,考虑了Hawkwing 评论关于通用哈希函数的内容...
def hash_for_file(path, algorithm=hashlib.algorithms[0], block_size=256*128, human_readable=True):
"""
Block size directly depends on the block size of your filesystem
to avoid performances issues
Here I have blocks of 4096 octets (Default NTFS)
Linux Ext4 block size
sudo tune2fs -l /dev/sda5 | grep -i 'block size'
> Block size: 4096
Input:
path: a path
algorithm: an algorithm in hashlib.algorithms
ATM: ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
block_size: a multiple of 128 corresponding to the block size of your filesystem
human_readable: switch between digest() or hexdigest() output, default hexdigest()
Output:
hash
"""
if algorithm not in hashlib.algorithms:
raise NameError('The algorithm "{algorithm}" you specified is '
'not a member of "hashlib.algorithms"'.format(algorithm=algorithm))
hash_algo = hashlib.new(algorithm) # According to hashlib documentation using new()
# will be slower then calling using named
# constructors, ex.: hashlib.md5()
with open(path, 'rb') as f:
for chunk in iter(lambda: f.read(block_size), b''):
hash_algo.update(chunk)
if human_readable:
file_hash = hash_algo.hexdigest()
else:
file_hash = hash_algo.digest()
return file_hash
不阅读完整内容就无法获取其md5。但您可以使用 update 函数逐块读取文件的内容。
m.update(a); m.update(b) 相当于 m.update(a+b)。
我认为下面的代码更Pythonic:
from hashlib import md5
def get_md5(fname):
m = md5()
with open(fname, 'rb') as fp:
for chunk in fp:
m.update(chunk)
return m.hexdigest()
我不喜欢循环。基于 Nathan Feger 的回答:
md5 = hashlib.md5()
with open(filename, 'rb') as f:
functools.reduce(lambda _, c: md5.update(c), iter(lambda: f.read(md5.block_size * 128), b''), None)
md5.hexdigest()
为 Django 实现 Yuval Adam 的答案:
import hashlib
from django.db import models
class MyModel(models.Model):
file = models.FileField() # Any field based on django.core.files.File
def get_hash(self):
hash = hashlib.md5()
for chunk in self.file.chunks(chunk_size=8192):
hash.update(chunk)
return hash.hexdigest()
正如@pseyfert 的评论中提到的;在Python 3.11及以上版本中,可以使用
hashlib.file_digest()
。虽然没有明确记录,但该函数内部使用了类似于已接受答案中的分块方法,从其源代码(第 230-236 行)可以看出。
该函数还提供了一个仅关键字参数
_bufsize
,默认值为 2^18 = 262,144 字节,用于控制分块的缓冲区大小;然而,考虑到它的前导下划线和缺失的文档,它可能更应该被视为一个实现细节。
在任何情况下,以下代码等效地重现接受的答案(除了不同的缓冲区大小):
import hashlib
with open("your_filename.txt", "rb") as f:
file_hash = hashlib.file_digest(f, "md5") # or `hashlib.md5` as 2nd arg
print(file_hash.digest())
print(file_hash.hexdigest()) # to get a printable str instead of bytes
我不确定这里是否有太多的喧闹。我最近遇到了 md5 和在 MySQL 中存储为 blob 的文件的问题,因此我尝试了各种文件大小和简单的 Python 方法,即:
FileHash = hashlib.md5(FileData).hexdigest()
对于 2 KB 到 20 MB 的文件大小范围,我无法检测到任何明显的性能差异,因此无需对哈希进行“分块”。无论如何,如果 Linux 必须访问磁盘,它可能至少会像普通程序员阻止它这样做的能力一样完成。事实上,这个问题与 md5 无关。如果您使用 MySQL,请不要忘记已经存在的 md5() 和 sha1() 函数。
import hashlib,re
opened = open('/home/parrot/pass.txt','r')
opened = open.readlines()
for i in opened:
strip1 = i.strip('\n')
hash_object = hashlib.md5(strip1.encode())
hash2 = hash_object.hexdigest()
print hash2