Хеширование файла в Python

101

Я хочу, чтобы python читал EOF, чтобы я мог получить соответствующий хеш, будь то sha1 или md5. Пожалуйста помоги. Вот что у меня есть на данный момент:

import hashlib

inputFile = raw_input("Enter the name of the file:")
openedFile = open(inputFile)
readFile = openedFile.read()

md5Hash = hashlib.md5(readFile)
md5Hashed = md5Hash.hexdigest()

sha1Hash = hashlib.sha1(readFile)
sha1Hashed = sha1Hash.hexdigest()

print "File Name: %s" % inputFile
print "MD5: %r" % md5Hashed
print "SHA1: %r" % sha1Hashed
user3358300
источник
6
а в чем проблема?
isedev
1
Я хочу, чтобы он мог хешировать файл. Мне нужно, чтобы он читал до EOF, независимо от размера файла.
user3358300
3
это именно то, что file.read()делает - читать весь файл.
isedev
В документации к read()методу написано?
Игнасио Васкес-Абрамс
Вы должны пройти через «что такое хеширование?».
Шариф Мамун

Ответы:

141

TL; DR используйте буферы, чтобы не использовать тонны памяти.

Я считаю, что мы подходим к сути вашей проблемы, когда рассматриваем последствия для памяти при работе с очень большими файлами . Мы не хотим, чтобы этот плохой мальчик потратил 2 гигабайта оперативной памяти на 2-гигабайтный файл, поэтому, как указывает пасторписти , мы должны обрабатывать эти большие файлы кусками!

import sys
import hashlib

# BUF_SIZE is totally arbitrary, change for your app!
BUF_SIZE = 65536  # lets read stuff in 64kb chunks!

md5 = hashlib.md5()
sha1 = hashlib.sha1()

with open(sys.argv[1], 'rb') as f:
    while True:
        data = f.read(BUF_SIZE)
        if not data:
            break
        md5.update(data)
        sha1.update(data)

print("MD5: {0}".format(md5.hexdigest()))
print("SHA1: {0}".format(sha1.hexdigest()))

Что мы сделали, так это обновили наши хэши этого плохого парня кусками по 64 КБ, следуя удобному методу обновления hashlib . Таким образом, мы используем намного меньше памяти, чем 2 ГБ, которые потребовались бы для хеширования всего парня сразу!

Вы можете проверить это с помощью:

$ mkfile 2g bigfile
$ python hashes.py bigfile
MD5: a981130cf2b7e09f4686dc273cf7187e
SHA1: 91d50642dd930e9542c39d36f0516d45f4e1af0d
$ md5 bigfile
MD5 (bigfile) = a981130cf2b7e09f4686dc273cf7187e
$ shasum bigfile
91d50642dd930e9542c39d36f0516d45f4e1af0d  bigfile

Надеюсь, это поможет!

Также все это описано в связанном вопросе справа: Получить хэш MD5 больших файлов в Python


Дополнение!

В общем, при написании python это помогает привыкнуть следовать pep-8 . Например, в Python переменные обычно разделяются подчеркиванием, а не верблюжьим регистром. Но это всего лишь стиль, и никого не волнуют эти вещи, кроме людей, которым приходится читать плохой стиль ... который, возможно, вы читаете этот код через годы.

Рэндалл Хант
источник
@ranman Привет, мне не удалось получить часть {0} ". format (sha1.hexdigest ()). Почему мы используем ее вместо того, чтобы просто использовать sha1.hexdigest ()?
Белиал,
@Belial Что не работает? Я в основном просто использовал это, чтобы различать два хэша ...
Рэндалл Хант
@ranman Все работает, просто никогда этим не пользовался и в литературе не встречал. "{0}". Format () ... мне неизвестно. :)
Белиал
1
Как выбрать BUF_SIZE?
Мартин Тома
1
Это не дает тех же результатов, что и shasumдвоичные файлы. Другой ответ, указанный ниже (тот, который использует memoryview), совместим с другими инструментами хеширования.
tedivm
61

Для правильного и эффективного вычисления хеш-значения файла (в Python 3):

  • Откройте файл в двоичном режиме (т.е. добавьте 'b'в файловый режим), чтобы избежать проблем с кодировкой символов и преобразованием окончания строки.
  • Не считывайте файл целиком в память, так как это пустая трата памяти. Вместо этого последовательно прочтите его блок за блоком и обновите хэш для каждого блока.
  • Устраните двойную буферизацию, т.е. не используйте буферизованный ввод-вывод, потому что мы уже используем оптимальный размер блока.
  • Используйте, readinto()чтобы избежать перемешивания буфера.

Пример:

import hashlib

def sha256sum(filename):
    h  = hashlib.sha256()
    b  = bytearray(128*1024)
    mv = memoryview(b)
    with open(filename, 'rb', buffering=0) as f:
        for n in iter(lambda : f.readinto(mv), 0):
            h.update(mv[:n])
    return h.hexdigest()
maxschlepzig
источник
2
Как узнать оптимальный размер блока?
Mitar
1
@Mitar, нижняя граница - это максимум физического блока (обычно 512 байт или 4 КБ для новых дисков) и размера страницы системы (4 КБ для многих систем, другие распространенные варианты: 8 КБ и 64 КБ). Затем вы в основном проводите сравнительный анализ и / или просматриваете опубликованные результаты тестов и связанную с ними работу (например, проверяете, что используется в текущем rsync / GNU cp / ...).
maxschlepzig 02
Будет resource.getpagesizeли здесь какая-то польза, если мы хотим попытаться оптимизировать его несколько динамически? А что mmap?
jpmc26
@ jpmc26, getpagesize () здесь не очень полезен - общие значения - 4 КиБ или 8 КиБ, что-то в этом диапазоне, то есть что-то намного меньшее, чем 128 КиБ - 128 КиБ обычно является хорошим выбором. mmap мало помогает в нашем случае, поскольку мы последовательно читаем весь файл от начала до конца. mmap имеет преимущества, когда шаблон доступа больше похож на случайный доступ, если к страницам обращаются более одного раза и / или если mmap упрощает управление буфером чтения.
maxschlepzig
3
Я протестировал как решение (1) @Randall Hunt, так и (2) ваше (в этом порядке, важно из-за файлового кеша) с файлом размером около 116 ГБ и алгоритмом sha1sum. Решение 1 было изменено, чтобы использовать буфер 20 * 4096 (PAGE_SIZE) и установить для параметра буферизации значение 0. Был изменен только алгоритм решения 2 (sha256 -> sha1). Результат: (1) 3 мин. 37,137 с. (2) 3 мин. 30,003 с. Собственный sha1sum в двоичном режиме: 3m31.395s
bioinfornatics
18

Я бы просто предложил:

def get_digest(file_path):
    h = hashlib.sha256()

    with open(file_path, 'rb') as file:
        while True:
            # Reading is buffered, so we can read smaller chunks.
            chunk = file.read(h.block_size)
            if not chunk:
                break
            h.update(chunk)

    return h.hexdigest()

Все остальные ответы здесь кажутся слишком сложными. Python уже выполняет буферизацию при чтении (в идеальном случае, или вы настраиваете эту буферизацию, если у вас есть больше информации о базовом хранилище), поэтому лучше читать по частям, хеш-функция находит идеальную, что делает ее быстрее или, по крайней мере, с меньшей нагрузкой на процессор для вычислить хеш-функцию. Поэтому вместо того, чтобы отключать буферизацию и пытаться эмулировать ее самостоятельно, вы используете буферизацию Python и контролируете то, что вы должны контролировать: то, что потребитель ваших данных считает идеальным, размер хэш-блока.

Митар
источник
Отличный ответ, но было бы неплохо, если бы вы подкрепили свои утверждения соответствующим документом: Python3 - open () и Python2 - open () . Даже учитывая разницу между ними, подход Python3 более сложен. Тем не менее, я действительно оценил ориентированность на потребителя!
Мурмель
hash.block_sizeдокументируется так же, как «размер внутреннего блока алгоритма хеширования». Хашлиб не считает это идеальным . Ничто в документации пакета не предполагает, что update()предпочитает hash.block_sizeразмер ввода. Он не использует меньше процессора, если вы его так называете. Ваш file.read()вызов приводит к созданию множества ненужных объектов и избыточных копий из файлового буфера в ваш новый объект байт фрагмента.
maxschlepzig
Хеши обновляют свое состояние по block_sizeчастям. Если вы не предоставляете их в этих кусках, они должны буферизоваться и ждать появления достаточного количества данных или внутренне разбивать данные на куски. Итак, вы можете просто справиться с этим снаружи, а затем упростите то, что происходит внутри. Я считаю это идеальным. См., Например: stackoverflow.com/a/51335622/252025
Митар,
Это block_sizeнамного меньше любого полезного размера для чтения. Кроме того, любые полезные размеры блока и чтения - это степени двойки. Таким образом, размер чтения делится на размер блока для всех операций чтения, кроме, возможно, последнего. Например, размер блока sha256 составляет 64 байта. Это означает, что update()он может напрямую обрабатывать ввод без какой-либо буферизации, кратной block_size. Таким образом, только если последнее чтение не делится на размер блока, он должен буферизовать до 63 байтов один раз. Следовательно, ваш последний комментарий неверен и не поддерживает утверждения, которые вы делаете в своем ответе.
maxschlepzig
Дело в том, что не нужно оптимизировать буферизацию, потому что это уже сделано Python при чтении. Таким образом, вам просто нужно решить, какое количество циклов вы хотите выполнить при хешировании по существующему буферу.
Mitar
6

Я запрограммировал модуль, который может хешировать большие файлы с помощью разных алгоритмов.

pip3 install py_essentials

Используйте модуль следующим образом:

from py_essentials import hashing as hs
hash = hs.fileChecksum("path/to/the/file.txt", "sha256")
phyyyl
источник
Это кроссплатформенный (Linux + Win)? Он работает с Python3? И все еще поддерживается?
Basj
5

Вот решение Python 3, POSIX (не Windows!), Которое используется mmapдля отображения объекта в память.

import hashlib
import mmap

def sha256sum(filename):
    h  = hashlib.sha256()
    with open(filename, 'rb') as f:
        with mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) as mm:
            h.update(mm)
    return h.hexdigest()
Антти Хаапала
источник
Наивный вопрос ... в чем преимущество использования mmapв этом сценарии?
Джонатан Б.
1
@JonathanB. большинство методов без необходимости создают bytesобъекты в памяти и вызывают readслишком много или слишком мало раз. Это отобразит файл непосредственно в виртуальную память и хэширует его оттуда - операционная система может отобразить содержимое файла непосредственно из буферного кеша в процесс чтения. Это означает, что это может быть во много раз быстрее, чем это
Антти Хаапала,
@JonathanB. Я провел тест, и в этом случае разница не такая значительная , мы говорим о ~ 15% по сравнению с наивным методом.
Антти Хаапала,
-2
import hashlib
user = input("Enter ")
h = hashlib.md5(user.encode())
h2 = h.hexdigest()
with open("encrypted.txt","w") as e:
    print(h2,file=e)


with open("encrypted.txt","r") as e:
    p = e.readline().strip()
    print(p)
Оме Мишра
источник
2
Вы в основном делаете то, echo $USER_INPUT | md5sum > encrypted.txt && cat encrypted.txtчто не касается хеширования файлов, особенно больших.
Мурмель
1
hashing! = encrypting
bugmenot123