Генерация контрольной суммы MD5 файла
Есть ли простой способ создания (и проверки) контрольных сумм MD5 для списка файлов в Python? (У меня есть небольшая программа, над которой я работаю, и я хотел бы подтвердить контрольные суммы файлов).
9 ответов
Вы можете использовать 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]
Опять можно поставить [:16]
после звонка hash_bytestr_iter(...)
если вы хотите только 128 битов дайджеста.
Я явно не добавляю ничего принципиально нового, но добавил этот ответ до того, как приступил к комментированию статуса:-), плюс регионы кода проясняют ситуацию - во всяком случае, специально для ответа на вопрос @Nemo из ответа Omnifarious:
Я немного подумал о контрольных суммах (пришел сюда в поисках предложений по размеру блоков, в частности), и обнаружил, что этот метод может быть быстрее, чем вы ожидаете. Взяв самый быстрый (но довольно типичный) 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 требуется около 30 мс для файла размером 11 МБ. Подходящий md5sum
функция (md5sum_read
в приведенном выше списке) очень похож на Omnifarious's:
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.
В 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.
hashlib.md5(pathlib.Path('path/to/file').read_bytes()).hexdigest()
В Python 3.11+ появился новый удобочитаемый и эффективный для памяти метод :
import hashlib
with open(path, "rb") as f:
digest = hashlib.file_digest(f, "md5")
print(digest.hexdigest())
Вы могли бы использовать1 , который просто используетsubprocess
звонитьopenssl
для macOS/Linux иCertUtil
для Windows и извлекает из вывода только дайджест:
Монтаж:
pip install simple-file-checksum
Использование:
>>> from simple_file_checksum import get_checksum
>>> get_checksum("path/to/file.txt")
'9e107d9d372bb6826bd81d3542a419d6'
>>> get_checksum("path/to/file.txt", algorithm="MD5")
'9e107d9d372bb6826bd81d3542a419d6'
The SHA1
,SHA256
,SHA384
, иSHA512
также поддерживаются алгоритмы.
1 Раскрытие информации: я авторsimple-file-checksum
.
вы можете использовать оболочку здесь.
from subprocess import check_output
#for windows & linux
hash = check_output(args='md5sum imp_file.txt', shell=True).decode().split(' ')[0]
#for mac
hash = check_output(args='md5 imp_file.txt', shell=True).decode().split('=')[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