अंत तक x बाइट्स तक एक फ़ाइल ऑब्जेक्ट से ब्लॉक पढ़ें

Nov 22 2020

मुझे लूप में 64KB का हिस्सा पढ़ने और उन्हें प्रोसेस करने की जरूरत है, लेकिन फाइल माइनस 16 बाइट्स के अंत में रुकें : अंतिम 16 बाइट्स tagमेटाडेटा हैं।

फ़ाइल सुपर बड़ी हो सकती है, इसलिए मैं इसे रैम में नहीं पढ़ सकता।

मुझे लगता है कि सभी समाधान थोड़ा अनाड़ी और / या unpythonic हैं।

with open('myfile', 'rb') as f:
    while True:
        block = f.read(65536)
        if not block:
            break
        process_block(block)

अगर 16 <= len(block) < 65536, यह आसान है: यह आखिरी ब्लॉक है। तो useful_data = block[:-16]औरtag = block[-16:]

यदि len(block) == 65536, इसका अर्थ तीन चीजें हो सकता है: पूर्ण ब्लॉक उपयोगी डेटा है। या कि यह 64KB ब्लॉक वास्तव में आखिरी ब्लॉक है , इसलिए useful_data = block[:-16]और tag = block[-16:]। या कि यह 64KB ब्लॉक केवल कुछ बाइट्स के एक और ब्लॉक (चलो 3 बाइट्स कहते हैं) के बाद है, इसलिए इस मामले में: useful_data = block[:-13]और tag = block[-13:] + last_block[:3]

इन सभी मामलों को अलग करने की तुलना में इस समस्या से अच्छे तरीके से कैसे निपटें?

ध्यान दें:

  • समाधान को किसी फ़ाइल के लिए open(...), बल्कि किसी io.BytesIO()ऑब्जेक्ट के लिए या दूर के SFTP द्वारा खोली गई फ़ाइल (के साथ pysftp) के लिए काम करना चाहिए ।

  • मैं फ़ाइल ऑब्जेक्ट आकार प्राप्त करने के बारे में सोच रहा था

    f.seek(0,2)
    length = f.tell()
    f.seek(0)
    

    फिर प्रत्येक के बाद

    block = f.read(65536)
    

    हम जान सकते हैं कि क्या हम अंत से दूर हैं length - f.tell(), लेकिन फिर से पूर्ण समाधान बहुत सुरुचिपूर्ण नहीं दिखता है।

जवाब

1 LiorCohen Nov 22 2020 at 20:31

आप बस हर पुनरावृत्ति में पढ़ सकते हैं min(65536, L-f.tell()-16)

कुछ इस तरह:

from pathlib import Path

L = Path('myfile').stat().st_size

with open('myfile', 'rb') as f:
    while True:    
        to_read_length = min(65536, L-f.tell()-16)
        block = f.read(to_read_length)
        process_block(block)
        if f.tell() == L-16
            break

इसे नहीं चलाया, लेकिन आशा है कि आपको इसका लाभ मिलेगा।

1 PresidentJamesK.Polk Nov 23 2020 at 00:16

निम्न विधि केवल इस तथ्य पर निर्भर करती है कि f.read()विधि धारा (ईओएस) के अंत में एक खाली बाइट ऑब्जेक्ट लौटाती है। इस प्रकार इसे बस के f.read()साथ बदलकर सॉकेट के लिए अपनाया जा सकता है s.recv()

def read_all_but_last16(f):
    rand = random.Random()  #  just for testing
    buf = b''
    while True:
        bytes_read = f.read(rand.randint(1, 40))  # just for testing
        # bytes_read = f.read(65536)
        buf += bytes_read
        if not bytes_read:
            break
        process_block(buf[:-16])
        buf = buf[-16:]
    verify(buf[-16:])

यह हमेशा bufईओएस के अंत तक 16 बाइट्स को छोड़कर काम करता है , फिर अंत में अंतिम 16 को संसाधित करता है। ध्यान दें कि यदि कम से कम 17 बाइट्स नहीं हैं, bufतो buf[:-16]खाली बाइट ऑब्जेक्ट वापस कर देता है।