แฮชไฟล์ใน Python


101

ฉันต้องการให้ 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

1
ฉันต้องการให้สามารถแฮชไฟล์ได้ ฉันต้องการให้อ่านจนถึง EOF ไม่ว่าขนาดไฟล์จะเป็นเท่าใดก็ตาม
user3358300

3
นั่นคือสิ่งที่file.read()ทำ - อ่านไฟล์ทั้งหมด
isedev

เอกสารสำหรับread()วิธีการระบุว่า?
Ignacio Vazquez-Abrams

คุณควรผ่าน "แฮชคืออะไร"
Sharif Mamun

คำตอบ:


141

TL; DR ใช้บัฟเฟอร์เพื่อไม่ใช้หน่วยความจำมากมาย

เราได้รับการปมของปัญหาของคุณผมเชื่อว่าเมื่อเราพิจารณาผลกระทบความทรงจำของการทำงานกับไฟล์ที่มีขนาดใหญ่มาก เราไม่ต้องการให้เด็กเลวคนนี้ปั่น RAM 2 กิ๊กสำหรับไฟล์ขนาด 2 กิกะไบต์ดังที่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()))

สิ่งที่เราทำคือเรากำลังอัปเดตแฮชของเด็กไม่ดีในชิ้น 64kb ของเราที่เราไปพร้อมกับสำรวยประโยชน์ hashlib ของวิธีการอัพเดต วิธีนี้ทำให้เราใช้หน่วยความจำน้อยกว่า 2gb ที่จะแฮชผู้ชายทั้งหมดในคราวเดียว!

คุณสามารถทดสอบได้ด้วย:

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

หวังว่าจะช่วยได้!

นอกจากนี้ทั้งหมดนี้มีรายละเอียดอยู่ในคำถามที่เชื่อมโยงทางด้านขวามือ: รับแฮช MD5 ของไฟล์ขนาดใหญ่ใน Python


ภาคผนวก!

โดยทั่วไปเมื่อเขียนหลามจะช่วยให้ได้รับเป็นนิสัยต่อไปนี้ห้าวหาญ-8 ตัวอย่างเช่นในตัวแปร python มักจะขีดล่างแยกออกจากกันไม่ใช่ camelCased แต่นั่นเป็นเพียงรูปแบบและไม่มีใครสนใจสิ่งเหล่านั้นจริงๆยกเว้นคนที่ต้องอ่านแบบแย่ ๆ ... ซึ่งอาจเป็นเพราะคุณอ่านรหัสนี้หลายปีต่อจากนี้


@ranman สวัสดีฉันไม่สามารถรับส่วน {0} ". format (sha1.hexdigest ()) ได้ทำไมเราถึงใช้มันแทนที่จะใช้ sha1.hexdigest ()?
Belial

@Belial อะไรไม่ทำงาน? ฉันใช้มันเพื่อแยกความแตกต่างระหว่างแฮชทั้งสองเป็นหลัก ...
Randall Hunt

@ranman ทุกอย่างใช้งานได้ฉันไม่เคยใช้สิ่งนี้และไม่เคยเห็นในวรรณกรรม "{0}". format () ... ฉันไม่รู้จัก :)
Belial

1
ควรเลือกBUF_SIZEอย่างไร?
Martin Thoma

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

1
@ มิทาร์ขอบเขตล่างคือค่าสูงสุดของบล็อกฟิสิคัล (ปกติ 512 ไบต์หรือ 4KiB พร้อมดิสก์รุ่นใหม่กว่า) และขนาดเพจของระบบ (4KiB ในหลายระบบตัวเลือกทั่วไปอื่น ๆ : 8KiB และ 64 KiB) จากนั้นคุณจะทำการเปรียบเทียบและ / หรือดูผลการวัดประสิทธิภาพที่เผยแพร่และงานที่เกี่ยวข้อง (เช่นตรวจสอบว่า rsync / GNU cp / ... ใช้ปัจจุบันใด)
maxschlepzig

จะresource.getpagesizeมีประโยชน์อะไรบ้างที่นี่ถ้าเราต้องการลองเพิ่มประสิทธิภาพแบบไดนามิกบ้าง? แล้วmmapล่ะ?
jpmc26

@ jpmc26, getpagesize () ไม่มีประโยชน์ที่นี่ - ค่าทั่วไปคือ 4 KiB หรือ 8 KiB บางอย่างในช่วงนั้นเช่นสิ่งที่เล็กกว่า 128 KiB - 128 KiB โดยทั่วไปเป็นทางเลือกที่ดี mmap ไม่ได้ช่วยอะไรมากในกรณีการใช้งานของเราเนื่องจากเราอ่านไฟล์ทั้งหมดจากด้านหน้าไปด้านหลัง mmap มีข้อดีเมื่อรูปแบบการเข้าถึงเป็นการเข้าถึงแบบสุ่มมากขึ้นเช่นหากมีการเข้าถึงเพจมากกว่าหนึ่งครั้งและ / หรือถ้าเป็น mmap จะทำให้การจัดการบัฟเฟอร์การอ่านง่าย
maxschlepzig

3
ฉันเปรียบเทียบทั้งโซลูชันของ (1) @Randall Hunt และ (2) ของคุณ (ตามลำดับนี้มีความสำคัญเนื่องจากแคชของไฟล์) ด้วยไฟล์ประมาณ 116GB และอัลกอริทึม sha1sum โซลูชันที่ 1 ได้รับการแก้ไขเพื่อใช้บัฟเฟอร์ 20 * 4096 (PAGE_SIZE) และตั้งค่าพารามิเตอร์การบัฟเฟอร์เป็น 0 โซลูชันที่ 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 - เปิด ()และPython2 - เปิด () แม้จะคำนึงถึงความแตกต่างระหว่างทั้งสองอย่างวิธีการของ Python3 ก็ซับซ้อนกว่า อย่างไรก็ตามฉันชอบมุมมองของผู้บริโภคเป็นศูนย์กลางจริงๆ!
Murmel

hash.block_sizeได้รับการจัดทำเป็นเอกสารเช่นเดียวกับ 'ขนาดบล็อกภายในของอัลกอริทึมแฮช' Hashlib ไม่พบว่ามันเหมาะ ไม่มีสิ่งใดในเอกสารประกอบแพ็กเกจที่แนะนำว่าupdate()ต้องการhash.block_sizeอินพุตขนาด มันไม่ได้ใช้ CPU น้อยลงถ้าคุณเรียกแบบนั้น การfile.read()เรียกของคุณนำไปสู่การสร้างอ็อบเจ็กต์ที่ไม่จำเป็นจำนวนมากและการคัดลอกที่ไม่จำเป็นจากบัฟเฟอร์ไฟล์ไปยังอ็อบเจ็กต์แบบก้อนใหม่ของคุณ
maxschlepzig

แฮชอัปเดตสถานะเป็นblock_sizeชิ้น ๆ หากคุณไม่ได้ให้ข้อมูลเหล่านี้ในกลุ่มเหล่านั้นพวกเขาจะต้องบัฟเฟอร์และรอให้ข้อมูลปรากฏเพียงพอหรือแยกข้อมูลที่กำหนดออกเป็นกลุ่มภายใน ดังนั้นคุณสามารถจัดการกับสิ่งนั้นได้จากภายนอกจากนั้นคุณก็ทำให้สิ่งที่เกิดขึ้นภายในง่ายขึ้น ฉันพบว่าสิ่งนี้เหมาะ ดูตัวอย่าง: stackoverflow.com/a/51335622/252025
Mitar

block_sizeมีขนาดเล็กกว่าขนาดอ่านใด ๆ ที่มีประโยชน์ นอกจากนี้บล็อกและขนาดการอ่านที่มีประโยชน์ใด ๆ ก็เป็นพลังสองอย่าง ดังนั้นขนาดการอ่านจะหารด้วยขนาดบล็อกสำหรับการอ่านทั้งหมดยกเว้นขนาดสุดท้าย ตัวอย่างเช่นขนาดบล็อก sha256 คือ 64 ไบต์ นั่นหมายความว่ามีความสามารถในการประมวลผลโดยตรงป้อนข้อมูลโดยไม่ต้องมีบัฟเฟอร์ถึงหลายupdate()block_sizeดังนั้นเฉพาะในกรณีที่การอ่านครั้งสุดท้ายไม่สามารถหารด้วยขนาดบล็อกได้จึงต้องบัฟเฟอร์ได้ถึง 63 ไบต์หนึ่งครั้ง ดังนั้นความคิดเห็นล่าสุดของคุณไม่ถูกต้องและไม่สนับสนุนการอ้างสิทธิ์ที่คุณทำในคำตอบของคุณ
maxschlepzig

ประเด็นก็คือเราไม่จำเป็นต้องปรับแต่งบัฟเฟอร์ให้เหมาะสมเพราะ Python ทำไปแล้วเมื่ออ่าน ดังนั้นคุณต้องตัดสินใจเกี่ยวกับจำนวนการวนซ้ำที่คุณต้องการทำเมื่อแฮชผ่านบัฟเฟอร์ที่มีอยู่นั้น
Mitar

6

ฉันได้ตั้งโปรแกรมโมดูลที่สามารถแฮชไฟล์ขนาดใหญ่ด้วยอัลกอริทึมที่แตกต่างกันได้

pip3 install py_essentials

ใช้โมดูลดังนี้:

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

มันข้ามแพลตฟอร์ม (Linux + Win) หรือไม่ มันทำงานร่วมกับ Python3 ได้หรือไม่? มันยังคงอยู่หรือไม่?
Basj

5

นี่คือ Python 3 โซลูชัน POSIX (ไม่ใช่ Windows!) ที่ใช้mmapในการแมปวัตถุลงในหน่วยความจำ

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

คำถามไร้เดียงสา ... ข้อดีของการใช้mmapในสถานการณ์นี้คืออะไร?
Jonathan B.

1
@ โจนาธานบี. วิธีการส่วนใหญ่สร้างbytesวัตถุในหน่วยความจำโดยไม่จำเป็นและเรียกreadจำนวนครั้งมากเกินไปหรือน้อยเกินไป สิ่งนี้จะแม็พไฟล์ลงในหน่วยความจำเสมือนโดยตรงและแฮชจากที่นั่น - ระบบปฏิบัติการสามารถแมปเนื้อหาไฟล์โดยตรงจากบัฟเฟอร์แคชเข้าสู่กระบวนการอ่าน ซึ่งหมายความว่าสิ่งนี้อาจเร็วขึ้นจากปัจจัยสำคัญเหนือสิ่งนี้
Antti Haapala

@ โจนาธานบี. ฉันได้ทำการทดสอบและความแตกต่างนั้นไม่สำคัญในกรณีนี้เรากำลังพูดถึงวิธีการไร้เดียงสาประมาณ ~ 15%
Antti Haapala

-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

1
hashing! = การเข้ารหัส
bugmenot123
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.