Băm tệp bằng Python


98

Tôi muốn python đọc tới EOF để tôi có thể nhận được hàm băm thích hợp, cho dù đó là sha1 hay md5. Xin vui lòng giúp đỡ. Đây là những gì tôi có cho đến nay:

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
và vấn đề là gì?
isedev

1
Tôi muốn nó có thể băm một tệp. Tôi cần nó để đọc cho đến khi EOF, bất kể kích thước tệp có thể là gì.
user3358300

3
đó chính xác là những gì file.read()- đọc toàn bộ tệp.
isedev

Tài liệu cho read()phương pháp cho biết?
Ignacio Vazquez-Abrams

Bạn nên xem qua "băm là gì?".
Sharif Mamun

Câu trả lời:


135

TL; DR sử dụng bộ đệm để không sử dụng hàng tấn bộ nhớ.

Tôi tin rằng chúng tôi đi đến điểm mấu chốt của vấn đề của bạn khi chúng tôi xem xét tác động của bộ nhớ khi làm việc với các tệp rất lớn . Chúng tôi không muốn cậu bé xấu này sử dụng 2 GB ram cho một tệp 2 gigabyte, vì vậy, như pasztorpisti đã chỉ ra, chúng tôi phải xử lý những tệp lớn hơn đó theo từng phần!

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

Những gì chúng tôi đã làm là chúng tôi đang cập nhật các mã băm của cậu bé xấu số này ở dạng khối 64kb khi chúng tôi thực hiện cùng với phương pháp cập nhật tiện dụng của hashlib . Bằng cách này, chúng tôi sử dụng ít bộ nhớ hơn rất nhiều so với 2gb mà nó sẽ mất để băm anh chàng cùng một lúc!

Bạn có thể kiểm tra điều này bằng:

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

Hy vọng rằng sẽ giúp!

Ngoài ra, tất cả những điều này được nêu trong câu hỏi được liên kết ở phía bên tay phải: Nhận băm MD5 của các tệp lớn bằng Python


Phụ lục!

Nói chung khi viết python, nó giúp bạn có thói quen làm theo pep-8 . Ví dụ: trong các biến python thường được phân tách bằng dấu gạch dưới chứ không phải là camelCased. Nhưng đó chỉ là phong cách và không ai thực sự quan tâm đến những điều đó ngoại trừ những người phải đọc văn phong tồi tệ ... đó có thể là bạn đọc mã này nhiều năm nữa.


@ranman Xin chào, tôi không thể lấy phần {0} ". format (sha1.hexdigest ()). Tại sao chúng tôi sử dụng nó thay vì chỉ sử dụng sha1.hexdigest ()?
Belial

@Belial Điều gì không hoạt động? Tôi chủ yếu chỉ sử dụng mà để phân biệt giữa hai băm ...
Randall Hunt

@ranman Mọi thứ đang hoạt động, tôi chưa bao giờ sử dụng cái này và chưa thấy nó trong tài liệu. "{0}". Format () ... tôi không biết. :)
Belial

1
Tôi nên chọn BUF_SIZEnhư thế nào?
Martin Thoma

1
Điều này không tạo ra kết quả giống như các shasumtệp nhị phân. Câu trả lời khác được liệt kê bên dưới (câu trả lời sử dụng memoryview) tương thích với các công cụ băm khác.
tedivm

61

Để tính toán chính xác và hiệu quả giá trị băm của tệp (trong Python 3):

  • Mở tệp ở chế độ nhị phân (tức là thêm 'b'vào mã tệp) để tránh các vấn đề về mã hóa ký tự và chuyển đổi cuối dòng.
  • Đừng đọc toàn bộ tập tin vào bộ nhớ, vì điều đó rất lãng phí bộ nhớ. Thay vào đó, hãy đọc tuần tự từng khối và cập nhật mã băm cho từng khối.
  • Loại bỏ bộ đệm kép, tức là không sử dụng IO có bộ đệm, vì chúng ta đã sử dụng kích thước khối tối ưu.
  • Sử dụng readinto()để tránh xáo trộn bộ đệm.

Thí dụ:

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
Làm thế nào để bạn biết kích thước khối tối ưu là gì?
Mitar

1
@Mitar, giới hạn dưới là mức tối đa của khối vật lý (theo truyền thống là 512 byte hoặc 4KiB với các đĩa mới hơn) và kích thước trang hệ thống (4KiB trên nhiều hệ thống, các lựa chọn phổ biến khác: 8KiB và 64 KiB). Sau đó, về cơ bản bạn thực hiện một số điểm chuẩn và / hoặc xem các kết quả điểm chuẩn đã xuất bản và công việc liên quan (ví dụ: kiểm tra xem hiện tại sử dụng rsync / GNU cp / ... nào).
maxschlepzig

Sẽ resource.getpagesizecó ích gì ở đây, nếu chúng ta muốn cố gắng tối ưu hóa nó một cách linh động? Và những gì về mmap?
jpmc 26

@ jpmc26, getpagesize () không hữu ích ở đây - các giá trị phổ biến là 4 KiB hoặc 8 KiB, một cái gì đó trong phạm vi đó, tức là một cái gì đó nhỏ hơn nhiều so với 128 KiB - 128 KiB nói chung là một lựa chọn tốt. mmap không giúp ích nhiều trong trường hợp sử dụng của chúng tôi vì chúng tôi đọc tuần tự toàn bộ tệp từ trước ra sau. mmap có lợi thế khi kiểu truy cập là truy cập ngẫu nhiên hơn như, nếu các trang được truy cập nhiều lần và / hoặc nếu nó thì mmap đơn giản hóa việc quản lý bộ đệm đọc.
maxschlepzig

3
Tôi đã đo điểm chuẩn cho cả giải pháp của (1) @Randall Hunt và (2) của bạn (theo thứ tự này, điều quan trọng là do bộ nhớ cache của tệp) với tệp có dung lượng khoảng 116GB và thuật toán sha1sum. Giải pháp 1 đã được sửa đổi để sử dụng bộ đệm 20 * 4096 (PAGE_SIZE) và đặt tham số đệm thành 0. Chỉ giải thuật 2 đã được sửa đổi (sha256 -> sha1). Kết quả: (1) 3m37.137s (2) 3m30.003s. Các sha1sum có nguồn gốc trong chế độ nhị phân: 3m31.395s
bioinfornatics

18

Tôi sẽ đề xuất đơn giản:

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

Tất cả các câu trả lời khác ở đây dường như phức tạp quá nhiều. Python đã lưu vào bộ đệm khi đọc (theo cách lý tưởng hoặc bạn định cấu hình bộ đệm đó nếu bạn có thêm thông tin về bộ nhớ cơ bản) và vì vậy tốt hơn nên đọc theo từng phần mà hàm băm tìm thấy lý tưởng để làm cho nó nhanh hơn hoặc ít nhất là ít tốn CPU hơn. tính toán hàm băm. Vì vậy, thay vì vô hiệu hóa bộ đệm và cố gắng tự mô phỏng nó, bạn sử dụng bộ đệm Python và kiểm soát những gì bạn nên kiểm soát: những gì người tiêu dùng dữ liệu của bạn thấy lý tưởng, kích thước khối băm.


Câu trả lời hoàn hảo, nhưng sẽ rất hay nếu bạn sao lưu các câu lệnh của mình bằng tài liệu liên quan: Python3 - open ()Python2 - open () . Ngay cả khi lưu ý đến sự khác biệt giữa cả hai, cách tiếp cận của Python3 phức tạp hơn. Tuy nhiên, tôi thực sự đánh giá cao quan điểm lấy người tiêu dùng làm trung tâm!
Murmel 19/09/19

hash.block_sizeđược ghi lại chỉ là 'kích thước khối nội bộ của thuật toán băm'. Hashlib không thấy nó lý tưởng . Không có gì trong tài liệu gói gợi ý rằng đầu vào có kích thước update()thích hợp hơn hash.block_size. Nó không sử dụng ít CPU hơn nếu bạn gọi nó như vậy. Cuộc file.read()gọi của bạn dẫn đến nhiều sáng tạo đối tượng không cần thiết và các bản sao thừa từ bộ đệm tệp sang đối tượng byte chunk mới của bạn.
maxschlepzig 19/09/19

Hash cập nhật trạng thái của chúng theo block_sizetừng phần. Nếu bạn không cung cấp chúng trong các phần đó, chúng phải đệm và đợi cho đủ dữ liệu xuất hiện hoặc chia dữ liệu đã cho thành các phần trong nội bộ. Vì vậy, bạn chỉ có thể xử lý điều đó ở bên ngoài và sau đó bạn đơn giản hóa những gì xảy ra bên trong. Tôi tìm thấy lý tưởng này. Xem ví dụ: stackoverflow.com/a/51335622/252025
Mitar

Các block_sizenhỏ hơn nhiều so với bất kỳ kích thước đọc hữu ích. Ngoài ra, bất kỳ khối hữu ích nào và kích thước đọc đều là lũy thừa của hai. Do đó, kích thước đọc được chia hết cho kích thước khối cho tất cả các lần đọc ngoại trừ có thể là lần đọc cuối cùng. Ví dụ, kích thước khối sha256 là 64 byte. Điều đó có nghĩa update()là có thể xử lý trực tiếp đầu vào mà không cần bất kỳ bộ đệm nào lên đến bất kỳ bội số nào block_size. Do đó, chỉ khi lần đọc cuối cùng không chia hết cho kích thước khối, nó phải đệm tối đa 63 byte một lần. Do đó, nhận xét cuối cùng của bạn không chính xác và không hỗ trợ các tuyên bố bạn đang đưa ra trong câu trả lời của mình.
maxschlepzig

Vấn đề là người ta không cần phải tối ưu hóa bộ đệm vì nó đã được Python thực hiện khi đọc. Vì vậy, bạn chỉ phải quyết định số lượng vòng lặp bạn muốn thực hiện khi băm trên bộ đệm hiện có đó.
Mitar

5

Tôi đã lập trình một mô-đun có thể băm các tệp lớn bằng các thuật toán khác nhau.

pip3 install py_essentials

Sử dụng mô-đun như thế này:

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

3

Đây là một giải pháp Python 3, POSIX (không phải Windows!) Sử dụng mmapđể ánh xạ đối tượng vào bộ nhớ.

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
Về cơ bản, bạn đang làm echo $USER_INPUT | md5sum > encrypted.txt && cat encrypted.txtđiều này không liên quan đến việc băm các tệp, đặc biệt là với những tệp lớn.
Murmel 19/09/19

băm! = mã hóa
bugmenot123
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.