Pythonでのファイルのハッシュ


98

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 2014

1
ファイルをハッシュできるようにしたい。ファイルサイズが何であれ、EOFまで読み取る必要があります。
user3358300 2014

3
それがまさに何をするかですfile.read()-ファイル全体を読みます。
isedev 2014

read()メソッドのドキュメントは言う?
Ignacio Vazquez-Abrams

あなたは「ハッシュとは何か」を経験するべきです。
Sharif Mamun 2014

回答:


135

TL; DRは、大量のメモリを使用しないようにバッファを使用します。

非常に大きなファイルを操作することによるメモリへの影響を考慮すると、問題の核心に到達すると思います。この悪い子が2ギガバイトのファイルに対して2ギガバイトのRAMをチャーンしないようにしたいので、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()))

これで、hashlibの便利なdandy 更新メソッドに合わせて、この不良少年のハッシュを64kbのチャンクに更新しました。この方法では、一度に男をハッシュするのに必要な2GBよりもはるかに少ないメモリを使用します!

これは次の方法でテストできます。

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

お役に立てば幸いです。

また、このすべては右側のリンクされた質問で概説されていますPythonで大きなファイルのMD5ハッシュを取得する


補遺!

一般に、Pythonを作成するときは、pep-8をフォローする習慣をつけるのに役立ちます。たとえば、Pythonでは通常、変数はキャメルケースではなくアンダースコアで区切られます。しかし、それは単なるスタイルであり、悪いスタイルを読まなければならない人々を除いて、誰もこれらのことを気にしません...これは、このコードを数年後に読むかもしれません。


@ranmanこんにちは、{0} "。format(sha1.hexdigest())の部分を取得できませんでした。sha1.hexdigest()を使用する代わりに、なぜそれを使用するのですか?
Belial

@Belial何がうまくいかなかったのですか?私は主に2つのハッシュを区別するためにそれを単に使用していました...
Randall Hunt

@ranmanすべてが機能しています。私はこれを使用したことがなく、文学でも見ていません。"{0}"。format()...私には不明です。:)
ベリアル

1
どのように選ぶべきですかBUF_SIZE
マーティントーマ2017

1
これは、shasumバイナリと同じ結果を生成しません。以下に示す他の回答(memoryviewを使用するもの)は、他のハッシュツールと互換性があります。
tedivm

61

ファイルのハッシュ値を正確かつ効率的に計算するには(Python 3の場合):

  • バイナリモードでファイルを開きます(つまり、追加 'b'文字エンコードと行末変換の問題を回避するためにする)
  • ファイル全体をメモリに読み込まないでください。メモリを浪費することになります。代わりに、ブロックごとに順次読み取り、各ブロックのハッシュを更新します。
  • すでに最適なブロックサイズを使用しているため、ダブルバッファリングを排除します。つまり、バッファIOを使用しません。
  • 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
最適なブロックサイズはどのようにしてわかりますか?
Mitar 2018年

1
@Mitar、下限は、物理ブロック(従来は512バイトまたは新しいディスクでは4KiB)とシステムのページサイズ(多くのシステムでは4KiB、その他の一般的な選択肢:8KiBおよび64 KiB)の最大値です。次に、基本的にベンチマークを行うか、公開されたベンチマークの結果と関連する作業を確認します(たとえば、現在のrsync / GNU cp / ...の使用状況を確認します)。
maxschlepzig 2018年

resource.getpagesize、我々はややそれを動的に最適化するために、試してみたかった場合は、ここで任意の使用であること?そして、どうmmapですか?
jpmc26

@ jpmc26、getpagesize()はここではそれほど役に立ちません。一般的な値は4 KiBまたは8 KiBで、その範囲内のものです。つまり、128 KiBよりはるかに小さいものです。128KiBが一般的に適切です。mmapは、完全なファイルを前から後ろに順番に読み取るため、このユースケースではあまり役に立ちません。mmapは、ページが複数回アクセスされる場合や、mmapが読み取りバッファーの管理を簡素化する場合など、アクセスパターンがよりランダムアクセスである場合に利点があります。
maxschlepzig 2018年

3
(1)@Randall Huntと(2)あなたのソリューション(この順序では、ファイルキャッシュのために重要です)の両方を約116GBのファイルとsha1sumアルゴリズムでベンチマークしました。20 * 4096(PAGE_SIZE)のバッファーを使用し、バッファリングパラメーターを0に設定するために、ソリューション1が変更されました。ソリューション2のみのアルゴリズムが変更されました(sha256-> sha1)。結果:(1)3m37.137s(2)3m30.003s。バイナリモードのネイティブの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は読み取り時にすでにバッファリングしています(理想的な方法で、または基盤となるストレージに関する詳細情報がある場合はそのバッファリングを構成します)。したがって、ハッシュ関数が理想的に見つけるチャンクをチャンクで読み取る方が高速であり、CPUの負荷を最小限に抑えます。ハッシュ関数を計算します。そのため、バッファリングを無効にして自分でエミュレートしようとするのではなく、Pythonバッファリングを使用して、制御対象を制御します。つまり、データの利用者が理想とするハッシュブロックサイズを制御します。


完璧な答えですが、関連するドキュメントであるPython3-open()およびPython2-open()でステートメントを裏付けるとよいでしょう。両者の違いを気にしても、Python3のアプローチはより洗練されています。それにもかかわらず、私は消費者中心の視点を本当に感謝しています!
Murmel

hash.block_size「ハッシュアルゴリズムの内部ブロックサイズ」と同様に文書化されています。Hashlib それを理想的と考えていません。パッケージドキュメントには、サイズの入力をupdate()優先することを示唆するものはありませんhash.block_size。このように呼び出すと、CPUの使用量が減りません。あなたのfile.read()多くの不要なオブジェクトの創造と新しいチャンクバイトオブジェクトへのファイルバッファから余分なコピーへの呼び出しリード。
maxschlepzig

ハッシュはそれらの状態をblock_sizeチャンクで更新します。それらをそれらのチャンクで提供しない場合、それらはバッファリングして十分なデータが表示されるのを待つか、与えられたデータを内部でチャンクに分割する必要があります。したがって、それを外側で処理するだけで、内部で発生することを単純化できます。私はこの理想を見つけます。例:stackoverflow.com/a/51335622/252025
Mitar

block_size任意の有用な読み取りサイズよりもはるかに小さいです。また、有用なブロックサイズと読み取りサイズは2の累乗です。したがって、読み取りサイズは、最後の読み取りを除くすべての読み取りのブロックサイズで割り切れます。たとえば、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")

3

以下はmmap、オブジェクトをメモリにマップするために使用するPython 3、POSIXソリューション(Windowsではありません!)です。

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()

-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にファイルのハッシュを処理しない、特に大きなファイルを処理していないことをしています。
Murmel

ハッシュ!=暗号化
bugmenot123 '22
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.