2011-10-19 36 views
16

Với python 2.7, mã sau tính toán md5 hexdigest của nội dung của tệp.Sử dụng hashlib để tính toán tiêu chuẩn md5 của tệp trong Python 3

(EDIT: tốt, không thực sự là câu trả lời đã hiển thị, tôi chỉ nghĩ vậy).

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

Bây giờ nếu tôi chạy mã mà sử dụng python3 nó nâng cao một ngoại lệ Lỗi Loại:

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

tôi đã tìm ra rằng tôi có thể làm cho rằng mã chạy với cả python2 và python3 thay đổi nó để:

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

Bây giờ tôi vẫn tự hỏi tại sao mã gốc ngừng hoạt động. Dường như khi mở một tệp bằng cách sử dụng công cụ sửa đổi chế độ nhị phân, nó trả về các số nguyên thay vì các chuỗi được mã hóa thành các byte (tôi nói rằng vì kiểu (buf) trả về int). Hành vi này có được giải thích ở đâu đó không?

+1

liên quan: http://stackoverflow.com/q/4949162/ – jfs

+1

Sẽ nhanh hơn nếu bạn đọc lớn hơn, gần kích thước khối tệp của hệ thống tệp hơn? (ví dụ 1024 byte trên Linux ext3 và 4096 byte hoặc nhiều hơn trên Windows NTFS) – rakslice

Trả lời

24

Tôi nghĩ rằng bạn muốn cho vòng lặp để thực hiện cuộc gọi liên tiếp để f.read(128). Điều đó có thể được thực hiện bằng iter()functools.partial():

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

Vâng, đó là chính xác những gì tôi đã cố gắng để làm. Cuối cùng tôi đã đạt được điều đó với một giải pháp ít thanh lịch hơn của bạn bằng cách sử dụng một máy phát điện. – kriss

+0

Điều này làm rò rỉ xử lý tệp trên một số triển khai Python. Ít nhất bạn nên gọi 'close'. – phihag

+1

Tôi đã thêm câu lệnh 'with' để đóng tệp đúng cách. – jfs

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

.. cập nhật liên tục băm với mỗi người trong số những người đầu tiên 128 byte giá trị của các tập tin. Kể từ khi lặp qua một số bytes tạo ra các đối tượng int, bạn nhận được các cuộc gọi sau gây ra lỗi mà bạn gặp phải trong Python3.

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

không phải là thứ bạn muốn.

Thay vào đó, bạn muốn:

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

Đây là toàn bộ RAM nếu bạn mở một tệp lớn. Đó là lý do tại sao chúng tôi đệm. –

+0

@fastreload Đã thêm điều đó;). Vì giải pháp ban đầu thậm chí không hoạt động đối với các tệp có> 128 byte, tôi không nghĩ rằng bộ nhớ là một vấn đề, nhưng tôi đã thêm một bộ đệm được đọc. – phihag

+0

Làm tốt rồi, nhưng OP tuyên bố rằng anh ta có thể sử dụng mã của mình trong Python 2.x và ngừng làm việc trên 3.x. Và tôi nhớ tôi đã tạo 1 bộ đệm byte để tính toán md5 của tệp iso 3 gb cho điểm chuẩn và nó không bị lỗi. Đặt cược của tôi là, python 2.7 có một cơ chế failsafe mà bất cứ điều gì người dùng nhập vào, kích thước bộ đệm tối thiểu không đi dưới một mức nhất định. bạn nói gì? –

1

cuối cùng tôi đã thay đổi mã của tôi để các phiên bản dưới đây (mà tôi tìm thấy dễ hiểu) sau khi đặt câu hỏi. Nhưng tôi có thể sẽ thay đổi nó thành phiên bản được đề xuất bởi 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

Điều này bây giờ sẽ làm việc nếu chiều dài tập tin không phải là một bội số của khối, đọc sẽ infact trả về một bộ đệm ngắn hơn trong lần đọc cuối cùng. Việc chấm dứt được đưa ra bởi một bộ đệm trống, đó là lý do tại sao điều kiện "không buf" trong mã ví dụ ở trên (hoạt động). – Mapio

+0

@Mapio: thực sự có một loại lỗi trong mã của tôi, nhưng không phải ở tất cả những gì bạn nói. Độ dài tệp không liên quan. Mã trên các công trình được cung cấp không có bộ đệm không hoàn chỉnh được đọc một phần. Nếu đọc một phần xảy ra, nó sẽ dừng quá sớm (nhưng lấy bộ đệm một phần vào tài khoản). Một phần đọc có thể xảy ra trong một số trường hợp, nói rằng nếu chương trình nhận được tín hiệu ngắt được quản lý trong khi đọc, sau đó tiếp tục đọc sau khi trở về từ gián đoạn. – kriss

+0

Vâng, trong nhận xét ở trên, khi nói về "mã ở trên" tôi đang đề cập đến phiên bản cũ. Điều này hiện tại đang làm việc (ngay cả khi nó không phải là giải pháp tốt nhất có thể). – kriss

Các vấn đề liên quan