我使用过hashlib(在Python 2.6 / 3.0中取代了md5),如果我打开一个文件并将其内容放入hashlib.md5()
函数中,它就可以正常工作。
问题在于非常大的文件,其大小可能超过RAM大小。
如何在不将整个文件加载到内存的情况下获取文件的MD5哈希?
我使用过hashlib(在Python 2.6 / 3.0中取代了md5),如果我打开一个文件并将其内容放入hashlib.md5()
函数中,它就可以正常工作。
问题在于非常大的文件,其大小可能超过RAM大小。
如何在不将整个文件加载到内存的情况下获取文件的MD5哈希?
Answers:
将文件分成8192字节的块(或128字节的其他倍数),然后使用连续将其馈送到MD5 update()
。
这利用了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
您需要以适当大小的块读取文件:
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>
rb
给open
函数)打开传递给该函数的文件。
hexdigest
代替digest
会产生一个十六进制哈希,看起来像大多数哈希示例一样。
if len(data) < block_size: break
吗?
open
始终打开位置设置为文件开头的新鲜文件句柄(除非您打开要追加的文件)。
下面,我结合了评论中的建议。谢谢大家!
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''(而不仅仅是“)。
128*md5.block_size
代替8192
。
md5.block_size
。
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等),您必须以二进制模式打开文件,因为您将对字节值求和:
要实现py27 / py3的可移植性,您应该使用以下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()
函数与前哨(空字符串)一起使用。
在这种情况下创建的迭代器将调用o [lambda函数],且每次对其
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
Django可接受答案的实现:
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()
我不喜欢循环。基于@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()
hashlib
s API无法与其余Python真正配合使用。例如,让我们来看看哪一个shutil.copyfileobj
几乎无法正常工作。我的下一个想法是fold
(aka reduce
)将可迭代对象折叠成单个对象。像例如哈希。hashlib
没有提供运算符,这使它有点麻烦。尽管如此,这里还是折叠了一个可迭代对象。
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
我不确定这里是否有太多大惊小怪的事情。我最近在md5上遇到问题,并且在MySQL上将这些文件存储为blob,因此我尝试了各种文件大小和简单的Python方法,即:
FileHash=hashlib.md5(FileData).hexdigest()
在文件大小为2Kb到20Mb的范围内,我无法检测到明显的性能差异,因此无需“分块”哈希。无论如何,如果Linux必须使用磁盘,那么它至少可以做到与普通程序员阻止磁盘使用的能力相同。碰巧,问题与md5无关。如果您使用的是MySQL,请不要忘记已经存在md5()和sha1()函数。