Хешування файлу в Python


100

Я хочу, щоб 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

6
і в чому проблема?
isedev

1
Я хочу, щоб він міг хеш-файл. Мені потрібно його прочитати до EOF, незалежно від розміру файлу.
user3358300

3
саме це і file.read()робить - прочитайте весь файл.
isedev

У документації до read()методу сказано?
Ігнасіо Васкес-Абрамс

Ви повинні пройти через "що таке хешування?".
Sharif Mamun

Відповіді:


140

TL; DR використовують буфери, щоб не використовувати тонни пам'яті.

Я вважаю, що ми дійдемо до суті вашої проблеми, коли розглянемо наслідки роботи з дуже великими файлами для пам'яті . Ми не хочемо, щоб цей поганий хлопчик пробив 2 концерти оперативної пам'яті для файлу розміром 2 гігабайти, тому, як зазначає pasztorpisti , нам доведеться мати справу з цими більшими файлами шматками!

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 змінні, як правило, виділяються підкресленням, а не camelCased. Але це просто стиль, і ніхто насправді не піклується про ці речі, крім людей, яким доводиться читати поганий стиль ... а це, можливо, ви читаєте цей код через роки.


@ranman Здрастуйте, я не міг отримати формат {0} »(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()

2
Звідки ви знаєте, який оптимальний розмір блоку?
Мітар

1
@Mitar, нижня межа - це максимум фізичного блоку (традиційно 512 байт або 4KB з новішими дисками) та розмір системної сторінки (4KiB у багатьох системах, інші поширені варіанти: 8KB та 64KB). Потім ви в основному виконуєте тестування та / або переглядаєте опубліковані результати тестування та відповідні роботи (наприклад, перевіряєте, що використовує поточний rsync / GNU cp / ...).
maxschlepzig 02.03.18

Чи 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) 3m37.137s (2) 3m30.003s. Рідна sha1sum у двійковому режимі: 3m31.395s
біоінформатика

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
MITAR

Значення block_sizeнабагато менше будь-якого корисного розміру читання. Крім того, будь-який корисний розмір блоку та зчитування є степенями в два. Таким чином, розмір зчитування ділиться на розмір блоку для всіх зчитувань, крім можливо останнього. Наприклад, розмір блоку sha256 становить 64 байти. Це означає, що update()він здатний безпосередньо обробляти вхідні дані без буферизації до кратного block_size. Таким чином, лише якщо останнє зчитування не ділиться на розмір блоку, він повинен буферувати до 63 байт, один раз. Отже, ваш останній коментар є неправильним і не підтримує твердження, які ви висловлюєте у своїй відповіді.
maxschlepzig

Справа в тому, що не потрібно оптимізувати буферизацію, оскільки це вже робиться Python під час читання. Отже, вам просто потрібно визначитися з кількістю циклу, який ви хочете зробити при хешуванні цього існуючого буфера.
Mitar

5

Я запрограмував модуль, який здатний хешувати великі файли з різними алгоритмами.

pip3 install py_essentials

Використовуйте модуль так:

from py_essentials import hashing as hs
hash = hs.fileChecksum("path/to/the/file.txt", "sha256")

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
хешування! = шифрування
bugmenot123
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.