【问题标题】:Read blocks from a file object until x bytes from the end从文件对象读取块,直到从末尾开始 x 个字节
【发布时间】:2020-11-22 20:13:43
【问题描述】:

我需要循环读取 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() 知道我们是否还远未结束,但同样,完整的解决方案看起来不是很优雅。

【问题讨论】:

    标签: python buffer block chunks file-read


    【解决方案1】:

    你可以在每次迭代中阅读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
    

    没有运行这个,但希望你能明白它的要点。

    【讨论】:

    • 不错的解决方案!我会将st_size 替换为f.seek(0, 2); L = f.tell(); f.seek(0),以便它适用于任何文件对象,而不仅仅是文件系统文件。
    • 我在犹豫是否在文件开头写tag(因此需要几个f.seek(),因为仅在写入文件的其余部分时才计算标签)或保留它最后+使用这个解决方案。你会在this situation@LiorCohen 做什么?
    • 如果您事先知道您的标签需要什么空间,我会将标签放在开头。当我知道它返回到 0 并写入它时。请注意不要覆盖第一个块。
    【解决方案2】:

    以下方法仅依赖于 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 的末尾保留 16 个字节直到 EOS,然后最终处理最后 16 个字节。请注意,如果 buf 中没有至少 17 个字节,则 buf[:-16] 返回空字节对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-17
      • 1970-01-01
      • 2011-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多