最後からxバイトまでファイルオブジェクトからブロックを読み取ります

Nov 22 2020

64KBのチャンクをループで読み取り、処理する必要がありますが、ファイルの終わりから16バイトを引いたところで停止します。最後の16バイトはtagメタデータです。

ファイルが非常に大きい可能性があるため、RAMですべてを読み取ることはできません。

私が見つけたすべての解決策は、少し不器用で、そして/または非Python的です。

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、それは可能性が3つのことを意味:フルブロックが有用なデータであること。それとも、この64キロバイトのブロックが実際にあることを最後のブロック、そう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()、ストリームの終了時に空のバイトオブジェクトを返すという事実のみに依存しています(EOS)。したがって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:])

それは、常にの終わりに16のバイトを残すことによって動作しbuf、最終的に少なくとも17のバイトに存在しない場合は、その最後の16ノートを処理して、EOSまでbuf、その後buf[:-16]、空のバイトオブジェクトを返します。