पायथन में बड़ी फ़ाइलों की MD5 हैश प्राप्त करें


188

मैंने हैशलीब का इस्तेमाल किया है (जो पायथन 2.6 / 3.0 में md5 की जगह लेता है) और अगर मैंने एक फ़ाइल खोली और hashlib.md5()फ़ंक्शन में इसकी सामग्री डाल दी, तो यह ठीक काम करता है।

समस्या बहुत बड़ी फ़ाइलों के साथ है कि उनका आकार रैम आकार से अधिक हो सकता है।

पूरी फ़ाइल को मेमोरी में लोड किए बिना किसी फाइल का MD5 हैश कैसे प्राप्त करें?


20
मैं rephrase: "कैसे एमडी 5 प्राप्त करने के लिए एक फ़ाइल को पूरी फ़ाइल को मेमोरी में लोड किए बिना है?"
XTL

जवाबों:


147

फ़ाइल को 8192-बाइट विखंडू (या 128 बाइट्स के कुछ अन्य) में तोड़ें और एमडी 5 का लगातार उपयोग करके उन्हें खिलाएं update()

यह इस तथ्य का लाभ उठाता है कि एमडी 5 में 128-बाइट डाइजेस्ट ब्लॉक (8192 128 × 64) हैं। चूंकि आप पूरी फ़ाइल को मेमोरी में नहीं पढ़ रहे हैं, इसलिए यह 8192 बाइट्स से अधिक मेमोरी का उपयोग नहीं करेगा।

पायथन 3.8+ में आप कर सकते हैं

import hashlib
with open("your_filename.txt", "rb") as f:
    file_hash = hashlib.md5()
    while chunk := f.read(8192):
        file_hash.update(chunk)
print(file_hash.digest())
print(file_hash.hexdigest())  # to get a printable str instead of bytes

81
आप बस के रूप में प्रभावी रूप से 128 (8192, 32768, आदि) के किसी भी कई के एक ब्लॉक आकार का उपयोग कर सकते हैं और यह एक बार में 128 बाइट्स पढ़ने की तुलना में बहुत तेज होगा।
jmanning2k

40
इस महत्वपूर्ण नोट के लिए धन्यवाद jmanning2k, 184MB फ़ाइल पर एक परीक्षण (0m9.230s, 0m2.547s, 0m2.429s) का उपयोग कर (128, 8192, 32768) लेता है, मैं 8192 का उपयोग करूंगा क्योंकि उच्च मूल्य गैर-ध्यान देने योग्य प्रभाव देता है।
JustRegisterMe

यदि आप कर सकते हैं, तो आपको hashlib.blake2bइसके बजाय का उपयोग करना चाहिए md5। MD5 के विपरीत, BLAKE2 सुरक्षित है, और यह और भी तेज़ है।
बोरिस

2
@ बोरिस, आप वास्तव में यह नहीं कह सकते कि BLAKE2 सुरक्षित है। आप बस इतना ही कह सकते हैं कि यह अभी तक टूटा नहीं है।
vy32

@ vy32 आप यह नहीं कह सकते कि यह निश्चित रूप से या तो टूट जाएगा। हम 100 वर्षों में देखेंगे, लेकिन यह एमडी 5 से कम से कम बेहतर है जो निश्चित रूप से असुरक्षित है।
बोरिस

220

आपको उपयुक्त आकार में फ़ाइल को पढ़ने की आवश्यकता है:

def md5_for_file(f, block_size=2**20):
    md5 = hashlib.md5()
    while True:
        data = f.read(block_size)
        if not data:
            break
        md5.update(data)
    return md5.digest()

नोट: सुनिश्चित करें कि आपने अपनी फ़ाइल को खुले में 'आरबी' के साथ खोल दिया है - अन्यथा आपको गलत परिणाम मिलेगा।

तो एक विधि में पूरे बहुत कुछ करने के लिए - जैसे कुछ का उपयोग करें:

def generate_file_md5(rootdir, filename, blocksize=2**20):
    m = hashlib.md5()
    with open( os.path.join(rootdir, filename) , "rb" ) as f:
        while True:
            buf = f.read(blocksize)
            if not buf:
                break
            m.update( buf )
    return m.hexdigest()

ऊपर दिया गया अपडेट फ्रेरिक राबे द्वारा प्रदान की गई टिप्पणियों पर आधारित था - और मैंने इसका परीक्षण किया और पाया कि यह मेरे पायथन 2.7.2 विंडोज़ इंस्टॉलेशन पर सही है

मैंने 'jacksum' टूल का उपयोग करके परिणामों को क्रॉस-चेक किया।

jacksum -a md5 <filename>

http://www.jonelo.de/java/jacksum/


29
यह ध्यान रखना महत्वपूर्ण है कि इस फ़ंक्शन के लिए जो फ़ाइल पास की गई है, उसे बाइनरी मोड में खोला जाना चाहिए, अर्थात फ़ंक्शन rbको पास करके open
फ्रेरिच राबे

11
यह एक सरल जोड़ है, लेकिन hexdigestइसके बजाय उपयोग करने से digestहेक्साडेसिमल हैश होगा जो कि हैश के अधिकांश उदाहरणों की तरह "दिखता है"।
tchaymore

यह नहीं होना चाहिए if len(data) < block_size: break?
एरिक कपलुन

2
एरिक, नहीं, यह क्यों होगा? लक्ष्य सभी बाइट्स को एमडी 5 तक फीड करना है, फ़ाइल के अंत तक। आंशिक ब्लॉक प्राप्त करने का मतलब यह नहीं है कि सभी बाइट्स को चेकसम को नहीं खिलाया जाना चाहिए।

2
@ user2084795 open हमेशा फ़ाइल की शुरुआत के लिए सेट की स्थिति के साथ एक ताज़ा फ़ाइल हैंडल खोलता है, (जब तक कि आप
स्टीव बार्न्स

110

नीचे मैंने टिप्पणियों से सुझाव शामिल किया है। थैंक यू अल!

अजगर <3.7

import hashlib

def checksum(filename, hash_factory=hashlib.md5, chunk_num_blocks=128):
    h = hash_factory()
    with open(filename,'rb') as f: 
        for chunk in iter(lambda: f.read(chunk_num_blocks*h.block_size), b''): 
            h.update(chunk)
    return h.digest()

अजगर 3.8 और ऊपर

import hashlib

def checksum(filename, hash_factory=hashlib.md5, chunk_num_blocks=128):
    h = hash_factory()
    with open(filename,'rb') as f: 
        while chunk := f.read(chunk_num_blocks*h.block_size): 
            h.update(chunk)
    return h.digest()

मूल पोस्ट

यदि आप अधिक पाइथोनिक के बारे में परवाह करते हैं (नहीं, जबकि 'सत्य') फ़ाइल को पढ़ने का तरीका इस कोड की जाँच करें:

import hashlib

def checksum_md5(filename):
    md5 = hashlib.md5()
    with open(filename,'rb') as f: 
        for chunk in iter(lambda: f.read(8192), b''): 
            md5.update(chunk)
    return md5.digest()

ध्यान दें कि ईओआर पर रुकने के लिए दिए गए पुनरावृत्ति के लिए पुनरावृति () फंक को एक खाली बाइट स्ट्रिंग की आवश्यकता है, क्योंकि रीड () रिटर्न बी '' (सिर्फ '' नहीं))।


17
अब भी बेहतर है, 128*md5.block_sizeइसके बजाय कुछ का उपयोग करें 8192
22

1
mrkj: मुझे लगता है कि आपकी डिस्क के आधार पर आपके रीड ब्लॉक आकार को चुनना अधिक महत्वपूर्ण है और फिर यह सुनिश्चित करना कि यह एक से अधिक है md5.block_size
हार्वे

6
b''वाक्य रचना मेरे लिए नया था। यहाँ समझाया गया
cod3monk3y

1
@ थोरसुमोनर: वास्तव में नहीं, लेकिन फ्लैश मेमोरी के लिए मेरे काम करने वाले इष्टतम ब्लॉक आकारों को खोजने से, मैं सुझाव दूंगा कि 32k जैसी कोई संख्या चुनें या 4, 8, या 16k द्वारा आसानी से विभाज्य कुछ। उदाहरण के लिए, यदि आपका ब्लॉक आकार 8k है, तो 32k पढ़ना सही ब्लॉक आकार में 4 रीड होगा। यदि यह 16 वर्ष का है, तो 2. लेकिन प्रत्येक मामले में, हम अच्छे हैं क्योंकि हम एक पूर्णांक कई ब्लॉकों को पढ़ रहे हैं।
हार्वे

1
"जबकि ट्रू" काफी पायथोनिक है।
जुरगेन ए। इरहार्ड

49

यहाँ मेरा संस्करण @Piotr Czapla की विधि है:

def md5sum(filename):
    md5 = hashlib.md5()
    with open(filename, 'rb') as f:
        for chunk in iter(lambda: f.read(128 * md5.block_size), b''):
            md5.update(chunk)
    return md5.hexdigest()

30

इस सूत्र में कई टिप्पणियों / उत्तरों का उपयोग करना, यहाँ मेरा समाधान है:

import hashlib
def md5_for_file(path, block_size=256*128, hr=False):
    '''
    Block size directly depends on the block size of your filesystem
    to avoid performances issues
    Here I have blocks of 4096 octets (Default NTFS)
    '''
    md5 = hashlib.md5()
    with open(path,'rb') as f: 
        for chunk in iter(lambda: f.read(block_size), b''): 
             md5.update(chunk)
    if hr:
        return md5.hexdigest()
    return md5.digest()
  • यह "पायथोनिक" है
  • यह एक फंक्शन है
  • यह निहित मूल्यों से बचा जाता है: हमेशा स्पष्ट लोगों को प्राथमिकता दें।
  • यह अनुमति देता है (बहुत महत्वपूर्ण) प्रदर्शन अनुकूलन

और अंत में,

- यह एक समुदाय द्वारा बनाया गया है, आपकी सलाह / विचारों के लिए धन्यवाद।


3
एक सुझाव: अपने md5 ऑब्जेक्ट को वैकल्पिक हैशिंग फ़ंक्शन की अनुमति देने के लिए फ़ंक्शन का एक वैकल्पिक पैरामीटर बनाएं, जैसे कि25 घंटे को आसानी से बदलने के लिए sha256। मैं इसे एक संपादन के रूप में भी प्रस्तावित करूंगा।
हॉकविंग

1
यह भी: पाचन मानव-पठनीय नहीं है। hexdigest () एक अधिक समझने योग्य, आमतौर पर पुन: उपयोग करने योग्य आउटपुट के साथ-साथ हैश के आसान विनिमय की अनुमति देता है
हॉकविंग

अन्य हैश प्रारूप प्रश्न के दायरे से बाहर हैं, लेकिन सुझाव अधिक सामान्य कार्य के लिए प्रासंगिक है। मैंने आपके दूसरे सुझाव के अनुसार "मानव पठनीय" विकल्प जोड़ा।
बस्तिन सेमेने

क्या आप इस बारे में विस्तार से बता सकते हैं कि यहाँ 'घंटा' कैसे काम कर रहा है?
EnemyBagJones

@EnemyBagJones 'hr' मानव पठनीय के लिए है। यह 32 char लंबाई वाली हेक्साडेसिमल अंकों की एक स्ट्रिंग लौटाता है: docs.python.org/2/library/md5.html#md5.md5.hexdigest
Bastien Semene

8

एक पायथन 2/3 पोर्टेबल समाधान

एक चेकसम (md5, sha1, आदि) की गणना करने के लिए, आपको बाइनरी मोड में फ़ाइल को खोलना होगा, क्योंकि आप बाइट्स विकल्पों के साथ योग करेंगे:

Py27 / py3 पोर्टेबल होने के लिए, आपको ioसंकुल का उपयोग करना चाहिए , जैसे:

import hashlib
import io


def md5sum(src):
    md5 = hashlib.md5()
    with io.open(src, mode="rb") as fd:
        content = fd.read()
        md5.update(content)
    return md5

यदि आपकी फाइलें बड़ी हैं, तो आप पूरी फाइल सामग्री को मेमोरी में स्टोर करने से बचने के लिए चंक्स द्वारा फाइल को पढ़ना पसंद कर सकते हैं:

def md5sum(src, length=io.DEFAULT_BUFFER_SIZE):
    md5 = hashlib.md5()
    with io.open(src, mode="rb") as fd:
        for chunk in iter(lambda: fd.read(length), b''):
            md5.update(chunk)
    return md5

यहां ट्रिक iter()को एक सेंटिनल (खाली स्ट्रिंग) के साथ फ़ंक्शन का उपयोग करना है ।

इस मामले में बनाया गया इटरेटर [लैंबडा फंक्शन] को कॉल करेगा जिसके प्रत्येक next()विधि के लिए कोई तर्क नहीं है; यदि लौटाया गया मूल्य प्रहरी के बराबर है, StopIterationतो उठाया जाएगा, अन्यथा मान वापस कर दिया जाएगा।

यदि आपकी फाइलें वास्तव में बड़ी हैं, तो आपको प्रगति जानकारी प्रदर्शित करने की आवश्यकता हो सकती है। आप कॉलबैक फ़ंक्शन को कॉल करके कर सकते हैं जो गणना बाइट्स की मात्रा को प्रिंट या लॉग करता है:

def md5sum(src, callback, length=io.DEFAULT_BUFFER_SIZE):
    calculated = 0
    md5 = hashlib.md5()
    with io.open(src, mode="rb") as fd:
        for chunk in iter(lambda: fd.read(length), b''):
            md5.update(chunk)
            calculated += len(chunk)
            callback(calculated)
    return md5

3

बास्टियन सेमेने कोड का रीमिक्स जो जेनेरिक हैशिंग फ़ंक्शन के बारे में हॉकिंग टिप्पणी को ध्यान में रखता है ...

def hash_for_file(path, algorithm=hashlib.algorithms[0], block_size=256*128, human_readable=True):
    """
    Block size directly depends on the block size of your filesystem
    to avoid performances issues
    Here I have blocks of 4096 octets (Default NTFS)

    Linux Ext4 block size
    sudo tune2fs -l /dev/sda5 | grep -i 'block size'
    > Block size:               4096

    Input:
        path: a path
        algorithm: an algorithm in hashlib.algorithms
                   ATM: ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
        block_size: a multiple of 128 corresponding to the block size of your filesystem
        human_readable: switch between digest() or hexdigest() output, default hexdigest()
    Output:
        hash
    """
    if algorithm not in hashlib.algorithms:
        raise NameError('The algorithm "{algorithm}" you specified is '
                        'not a member of "hashlib.algorithms"'.format(algorithm=algorithm))

    hash_algo = hashlib.new(algorithm)  # According to hashlib documentation using new()
                                        # will be slower then calling using named
                                        # constructors, ex.: hashlib.md5()
    with open(path, 'rb') as f:
        for chunk in iter(lambda: f.read(block_size), b''):
             hash_algo.update(chunk)
    if human_readable:
        file_hash = hash_algo.hexdigest()
    else:
        file_hash = hash_algo.digest()
    return file_hash

0

पूर्ण सामग्री को पढ़े बिना आप इसे md5 नहीं कर सकते। लेकिन आप ब्लॉक द्वारा फ़ाइलों की सामग्री को पढ़ने के लिए अपडेट फ़ंक्शन का उपयोग कर सकते हैं ।
m.update (क); m.update (b) m.update (a + b) के बराबर है


0

मुझे लगता है कि निम्नलिखित कोड अधिक पायथोनिक है:

from hashlib import md5

def get_md5(fname):
    m = md5()
    with open(fname, 'rb') as fp:
        for chunk in fp:
            m.update(chunk)
    return m.hexdigest()

-1

Django के लिए स्वीकृत उत्तर का कार्यान्वयन:

import hashlib
from django.db import models


class MyModel(models.Model):
    file = models.FileField()  # any field based on django.core.files.File

    def get_hash(self):
        hash = hashlib.md5()
        for chunk in self.file.chunks(chunk_size=8192):
            hash.update(chunk)
        return hash.hexdigest()

-1

मुझे लूप पसंद नहीं है। @ नथन फ़ीगर पर आधारित:

md5 = hashlib.md5()
with open(filename, 'rb') as f:
    functools.reduce(lambda _, c: md5.update(c), iter(lambda: f.read(md5.block_size * 128), b''), None)
md5.hexdigest()

एक संभावित और एक स्पष्ट लूप को बदलने के लिए संभावित कारण क्या है जिसमें कई लैंबडैस युक्त एक फंक्शनलबुलस एबेरेशन है? मुझे यकीन नहीं है कि अगर प्रोग्रामिंग पर कोई सम्मेलन नहीं हुआ है तो यह नहीं टूटा है।
नल्लाथिरल

मेरी मुख्य समस्या यह थी कि hashlibएपीआई वास्तव में पायथन के बाकी हिस्सों के साथ अच्छा नहीं खेलता है। उदाहरण के लिए, आइए हम shutil.copyfileobjकाम करने में विफल रहे। मेरा अगला विचार था fold(उर्फ reduce) जो एकल वस्तुओं में एक साथ पुनरावृत्तियों को मोड़ता है। जैसे हैश। hashlibऑपरेटरों को प्रदान नहीं करता है जो इसे थोड़ा बोझिल बनाता है। फिर भी यहाँ एक पुनरावृत्तियों को मोड़ रहे थे।
सेबस्टियन वैग्नर

-3
import hashlib,re
opened = open('/home/parrot/pass.txt','r')
opened = open.readlines()
for i in opened:
    strip1 = i.strip('\n')
    hash_object = hashlib.md5(strip1.encode())
    hash2 = hash_object.hexdigest()
    print hash2

1
कृपया, उत्तर में कोड को प्रारूपित करें, और उत्तर देने से पहले इस खंड को पढ़ें: stackoverflow.com/help/how-to-answer
फ़ारसाइड

1
यह सही ढंग से काम नहीं करेगा क्योंकि यह फाइल को टेक्स्ट मोड लाइन में लाइन से पढ़ रहा है और फिर उसके साथ गड़बड़ कर रहा है और प्रत्येक छीन, एनकोडेड, लाइन के md5 को प्रिंट कर रहा है!
स्टीव बार्न्स

-4

मुझे यकीन नहीं है कि थोड़ा बहुत यहाँ उपद्रव नहीं है। मुझे हाल ही में md5 और MySQL पर ब्लब्स के रूप में संग्रहीत फ़ाइलों के साथ समस्या थी, इसलिए मैंने विभिन्न फ़ाइल आकारों और सीधे पायथन दृष्टिकोण, अर्थात के साथ प्रयोग किया:

FileHash=hashlib.md5(FileData).hexdigest()

मैं फ़ाइल आकार 2Kb से 20Mb की सीमा के साथ कोई ध्यान देने योग्य प्रदर्शन अंतर का पता लगा सकता है और इसलिए हैशिंग को 'चंक' करने की कोई आवश्यकता नहीं है। वैसे भी, अगर लिनक्स को डिस्क पर जाना है, तो यह संभवतः कम से कम और साथ ही औसत प्रोग्रामर की क्षमता को ऐसा करने से रोकने के लिए करेगा। जैसा कि हुआ था, समस्या का md5 से कोई लेना-देना नहीं था। यदि आप MySQL का उपयोग कर रहे हैं, तो md5 () और sha1 () फ़ंक्शन पहले से ही न भूलें।


2
यह सवाल का जवाब नहीं दे रहा है और 20 एमबी को शायद ही एक बहुत बड़ी फ़ाइल माना जाता है जो रैम में फिट नहीं हो सकता है जैसा कि यहां चर्चा की गई है।
क्रिस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.