[python] Python에서 큰 파일의 MD5 해시 가져 오기
나는 hashlib (Python 2.6 / 3.0에서 md5를 대체 함)를 사용했으며 파일을 열고 내용을 hashlib.md5()
작동 시키면 정상적으로 작동했습니다 .
문제는 크기가 RAM 크기를 초과 할 수있는 매우 큰 파일에 있습니다.
전체 파일을 메모리에로드하지 않고 파일의 MD5 해시를 얻는 방법은 무엇입니까?
답변
파일을 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>
답변
아래에 의견의 제안을 통합했습니다. 알 감사합니다!
파이썬 <3.7
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()
파이썬 3.8 이상
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 ( ‘truele’없음) 방법에 관심이 있다면 다음 코드를 확인하십시오.
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()
read ()가 b ”를 반환하기 때문에 iter () func은 반환 된 반복자가 EOF에서 정지하기 위해 빈 바이트 문자열을 필요로합니다.
답변
@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 [람다 함수]를 호출합니다
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
답변
일반적인 해싱 함수에 대한 Hawkwing 주석을 고려한 Bastien Semene 코드의 리믹스 …
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