Глава 23 · Часть IV · Реализуем SafeSort

SHA-256 и хеш содержимого файла

Одинаковое содержимое всегда даёт одинаковый дайджест SHA-256. Поблочное чтение не требует держать в памяти весь файл разом.

SafeSort · Часть 4 из 6Реализация
Байты файласодержимое целикомSHA-256hashlib.sha256()64 hex-символадайджест

Хеш-функция превращает содержимое файла произвольного размера в строку фиксированной длины — дайджест. SafeSort использует SHA-256 из модуля hashlib: одинаковые байты всегда дают одинаковый дайджест, а разные дайджесты гарантированно значат разное содержимое. Хеширование many-to-one: возможных входов больше, чем 256-битных результатов, поэтому разные входы математически могут иметь один дайджест. Совпадающий дайджест служит сильным фильтром, но не доказывает равенство файлов. Поскольку SafeSort перемещает настоящие файлы пользователя, он не останавливается на совпадении дайджеста — следующая страница показывает последний шаг, который превращает совпадение хеша в подтверждённый дубликат.

src/safesort/duplicates.py
def sha256_stream(stream: BinaryIO, chunk_size: int = 1024 * 1024) -> str:
    digest = hashlib.sha256()
    while chunk := stream.read(chunk_size):
        digest.update(chunk)
    return digest.hexdigest()
Хеширование и шифрование решают разные задачи
SHA-256 спроектирован так, чтобы поиск прообраза по дайджесту был вычислительно неосуществим на практике. Это инженерное свойство стойкости, а не математическое утверждение «обратить невозможно». Хеш-функция помогает ответить на вопрос «может ли это быть одинаковое содержимое», а не скрывает его. SafeSort использует SHA-256 исключительно для сравнения файлов, а не для защиты данных.

При лавинном эффекте малое изменение входа обычно меняет много битов выхода; для идеализированного поведения ожидают примерно половину выходных битов. Это не означает, что в каждом опыте обязана измениться каждая шестнадцатеричная цифра.

Зачем читать файл частями

Цикл while chunk := file.read(chunk_size) читает файл не целиком, а блоками по мегабайту, и каждый блок сразу добавляет к дайджесту через digest.update(). Если бы функция читала файл одним вызовом file.read(), для файла в несколько гигабайт программе пришлось бы держать в оперативной памяти всё его содержимое разом. При поэтапном чтении в памяти в любой момент находится только один блок, независимо от размера файла целиком.

Проверка вручную
>>> from pathlib import Path
>>> from safesort.duplicates import sha256_file
>>> sha256_file(Path("otchet.pdf"))
'784cc58b2286b83f67f58ffb1968ca4b80d1d0615863ad9b1ce9c3d05666f4e'
Практика: хеш содержимого по частям
Интерактивный ноутбук в браузере: Python 3.14 через Pyodide, без установки
Открыть практику →
Официальная документация
hashlib — Secure hashes and message digests
NIST FIPS 180-4 — Secure Hash Standard