2011-10-19 99 views
16

python 2.7では、次のコードはファイルの内容のmD5の16進数を計算します。hashlibを使ってPython 3のファイルのmd5ダイジェストを計算する

(EDIT:よく、答えは表示されていませんが、私はそう考えていました)

import hashlib 

def md5sum(filename): 
    f = open(filename, mode='rb') 
    d = hashlib.md5() 
    for buf in f.read(128): 
     d.update(buf) 
    return d.hexdigest() 

今私はのpython3を使用してそのコードを実行した場合、それはTypeError例外上げる:

d.update(buf) 
TypeError: object supporting the buffer API required 

を私はpython2とのpython3の両方で実行されるコードは、それを変更させることができることを考え出し:

def md5sum(filename): 
    f = open(filename, mode='r') 
    d = hashlib.md5() 
    for buf in f.read(128): 
     d.update(buf.encode()) 
    return d.hexdigest() 

今でも元のコードが動作しなくなったのは不思議です。バイナリモード修飾子を使用してファイルを開くと、バイトとしてエンコードされた文字列の代わりに整数が返されるようです(これは、type(buf)がintを返すためです)。この動作はどこかで説明されていますか?

+1

関連:http://stackoverflow.com/q/4949162/ – jfs

+1

それがより速くなるでしょう、あなたは大規模で、ファイルシステムのファイルのブロック・サイズに近い読みましたか? (例えば、Linuxのext3では1024バイト、Windows NTFSでは4096バイト以上) – rakslice

答えて

24

は、私はあなたがf.read(128)への連続呼び出しを行うためのループを望んでいたと思います。それは)()とfunctools.partial(ITERを使用して行うことができます。

import hashlib 
from functools import partial 

def md5sum(filename): 
    with open(filename, mode='rb') as f: 
     d = hashlib.md5() 
     for buf in iter(partial(f.read, 128), b''): 
      d.update(buf) 
    return d.hexdigest() 

print(md5sum('utils.py')) 
+0

はい、まさに私がやろうとしていたことです。私は最終的に、ジェネレーターを使用してあなたよりもあまりエレガントなソリューションでそれを達成しました。 – kriss

+0

これは、いくつかのPython実装でファイルハンドルをリークします。少なくともcloseを呼び出す必要があります。 – phihag

+1

ファイルを適切に閉じるために 'with'ステートメントを追加しました。 – jfs

10
for buf in f.read(128): 
    d.update(buf) 

..は、ファイルの最初の128のバイト値のそれぞれにハッシュを順次更新します。 bytesを反復処理するとintオブジェクトが生成されるため、Python3で発生したエラーを引き起こす次の呼び出しが発生します。

d.update(97) 
d.update(98) 
d.update(99) 
d.update(100) 

これはあなたが望むものではありません。

代わりに、あなたがしたい:

def md5sum(filename): 
    with open(filename, mode='rb') as f: 
    d = hashlib.md5() 
    while True: 
     buf = f.read(4096) # 128 is smaller than the typical filesystem block 
     if not buf: 
     break 
     d.update(buf) 
    return d.hexdigest() 
+1

巨大なファイルを開くとRAM全体が食べられます。だから我々はバッファリングする。 –

+0

@fastreloadすでに追加されています;)。元のソリューションは128バイトを超えるファイルでも機能しなかったので、メモリは問題ではないと思いますが、とにかくバッファリングされた読み取りを追加しました。 – phihag

+0

さて、OPは彼がPython 2.xで自分のコードを使用できると主張し、3.xでの作業をやめました。そして私は、ベンチマークのために3GBのisoファイルのmd5を計算するために1バイトのバッファを作成したことを思い出し、それは失敗しなかったことを覚えています。私の賭けは、Python 2.7には、ユーザの入力が何であれ、最小バッファサイズが一定のレベルを下回らないというフェールセーフ機構があります。あなたは何を言っていますか? –

1

私は最終的に質問をした後、(私が理解しやすい見つけること)以下のバージョンに私のコードを変更しました。しかし、私はおそらくRaymond Hetting unsing functools.partialが提案したバージョンに変更する予定です。

import hashlib 

def chunks(filename, chunksize): 
    f = open(filename, mode='rb') 
    buf = "Let's go" 
    while len(buf): 
     buf = f.read(chunksize) 
     yield buf 

def md5sum(filename): 
    d = hashlib.md5() 
    for buf in chunks(filename, 128): 
     d.update(buf) 
    return d.hexdigest() 
+0

これは、ファイルの長さがchunksizeの倍数でない場合に有効になります。readは、最後の読み込みでより短いバッファを返します。終了は空のバッファによって与えられます。そのため、上記の例のコードでは "not buf"の条件が有効です。 – Mapio

+0

@Mapio:私のコードには本当にバグがありますが、あなたが言うところにはまったくありません。ファイルの長さは関係ありません。上記のコードは、不完全なバッファを返す部分的な読み取りがないという条件で動作します。パーシャルリードが発生した場合、パーシャルバッファを考慮してすぐに停止します。場合によっては、プログラムが読み出し中に管理された割り込み信号を受け取った場合、割り込みから復帰した後に読み出しを続けると、部分的な読み出しが行われることがあります。 – kriss

+0

まあ、上のコメントでは、 "上のコード"と言えば、私は古いバージョンを参照しています。この現在のものは、(たとえそれが最良の解決策ではないとしても)今働いています。 – kriss

関連する問題