끝에서 x 바이트까지 파일 객체에서 블록 읽기
루프에서 64KB 청크를 읽고 처리해야하지만 파일 끝에서 16 바이트를 빼면 중지됩니다 . 마지막 16 바이트는 tag
메타 데이터입니다.
파일이 너무 커서 RAM에서 모두 읽을 수 없습니다.
내가 찾은 모든 솔루션은 약간 서 투르거나 비 파이썬 적입니다.
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()
있지만 전체 솔루션이 매우 우아하게 보이지는 않습니다.
답변
모든 반복에서 읽을 수 있습니다. 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
이것을 실행하지 않았지만 요점을 이해하기를 바랍니다.
다음 메서드 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:])
buf
EOS 가 끝날 때까지 항상 16 바이트를 남겨두고 마지막으로 마지막 16 바이트를 처리하는 방식으로 작동 합니다. 최소 17 바이트가 없으면 빈 바이트 개체 buf
를 buf[:-16]
반환합니다.