从文件对象中读取块,直到从末尾开始 x 个字节
Read blocks from a file object until x bytes from the end
我需要循环读取 64KB 的块,并处理它们,但是 在文件末尾减去 16 字节后停止:最后 16 字节是 tag
元数据。
文件可能超大,所以我无法在 RAM 中全部读取。
我找到的所有解决方案都有点笨拙and/or 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()
离结束还远,但是完整的解决方案看起来也不是很优雅。
你可以在每次迭代中阅读 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()
方法 returns 流结束时的空字节对象 (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
末尾保留 16 个字节,直到 EOS,然后最后处理最后 16 个字节。请注意,如果 buf
中没有至少 17 个字节,则 buf[:-16]
returns 空字节对象。
我需要循环读取 64KB 的块,并处理它们,但是 在文件末尾减去 16 字节后停止:最后 16 字节是 tag
元数据。
文件可能超大,所以我无法在 RAM 中全部读取。
我找到的所有解决方案都有点笨拙and/or 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()
离结束还远,但是完整的解决方案看起来也不是很优雅。
你可以在每次迭代中阅读 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()
方法 returns 流结束时的空字节对象 (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
末尾保留 16 个字节,直到 EOS,然后最后处理最后 16 个字节。请注意,如果 buf
中没有至少 17 个字节,则 buf[:-16]
returns 空字节对象。