登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  python教程

Python bz2.BZ2Decompressor 怎么按输入需求循环解压数据流

来源:17golang原创

时间:2026-11-11 08:08:29 250浏览 收藏

用 bz2.BZ2Decompressor 处理网络或文件分块时,关键不是“每次读一块、解压一次”,而是看解压器当前还需不需要输入:needs_input=False 表示内部仍有输出可以取,应该继续调用 decompress(b"");needs_input=True 才说明可以读取下一块压缩数据。流结束后看 eof,同一块输入末尾的额外字节则从 unused_data 取出。

官方文档:https://docs.python.org/3/library/bz2.html#bz2.BZ2Decompressor.needs_input

下面的写法只讨论单个 bzip2 压缩流的增量输入,不负责识别和拼接多成员流。

先分清 needs_input 到底在提示什么

needs_input 不是“上一次调用有没有传入 bytes”的记录,而是解压器的供给状态。正常情况下,传入一块数据后有两种分支:

  • True:这块数据已经用完,下一轮可以从上游读取更多压缩字节。
  • False:输入可能已经被内部缓冲,但还有未返回的解压结果,先不要读取下一块。

第二种情况最容易造成误判。尤其给 decompress() 传入非负的 max_length 时,本次最多只返回指定数量的明文,剩余输出要用空输入继续取。把 needs_input=False 当成“需要继续读网络”会让循环过早叠加输入,调试时也看不出究竟是输入不足还是输出未排空。

BZ2Decompressor 输入供给与内部输出缓冲的静态结构图
图1:结构说明图,展示压缩输入、BZ2Decompressor、内部输出缓冲和明文接收端之间的关系;这是静态说明图,不是运行截图。

用 max_length 把一次解压拆成可控的输出片段

如果调用 decompress(chunk, max_length=64),一次返回最多 64 字节的明文。当解压器还能继续产生明文时,needs_input 会保持为 False。此时下一次调用应该传入 b"",而不是重新读取压缩源。

import bz2


def decompress_one_stream(chunks, max_length=64):
    """按解压器状态消费单个 bzip2 流,并返回明文和尾部字节。"""
    decompressor = bz2.BZ2Decompressor()
    output = bytearray()
    tail = b""

    for chunk in chunks:
        # 空块不提供新输入,避免把它误当成流结束。
        if not chunk:
            continue

        # 只有需要输入时才喂入新的压缩块。
        output.extend(decompressor.decompress(chunk, max_length=max_length))

        # max_length 截断了输出时,用空输入排出内部缓冲。
        while not decompressor.eof and not decompressor.needs_input:
            output.extend(decompressor.decompress(b"", max_length=max_length))

        if decompressor.eof:
            # 当前块中位于流结束标记之后的字节由上层决定如何处理。
            tail = decompressor.unused_data
            break

    if not decompressor.eof:
        # 输入结束却没有 eof,说明压缩流可能被截断。
        raise EOFError("bzip2 stream ended before eof")

    return bytes(output), tail

这段循环的分工很明确:外层 for 只负责提供新的压缩块,内层 while 只负责排空已经进入解压器的输出。内层退出且尚未 eof 时,正常原因就是 needs_input=True,下一轮再从上游读取。

eof 和 unused_data 要放在什么位置判断

eof=True 表示当前压缩流的结束标记已经到达。判断到它以后应立即停止调用这个 BZ2Decompressor,因为再次解压会抛出 EOFError。如果结束标记后面还有字节,Python 会把它们放到 unused_data,而不是并入当前流的明文。

这两个状态的边界可以这样记:

状态下一动作不要做什么
needs_input=False用 b""继续取输出不要立刻读取下一块
needs_input=True向上游请求压缩数据不要假定已经到 EOF
eof=True保存 unused_data并结束当前对象不要再次调用 decompress()
输入耗尽但 eof=False报告截断或等待更多输入不要把空块当成正常结束
BZ2Decompressor needs_input、eof 与 unused_data 的状态边界静态图
图2:边界说明图,展示 needs_input、eof 和 unused_data 分别对应的输入、输出与尾部数据边界;这是静态说明图,不是运行截图。

网络分块场景中的三个容易踩坑点

第一,不要按“收到一块就必须读下一块”的节奏写死循环;当 needs_input=False 时,当前对象内部已经有数据可返回。第二,不要把 eof=False 直接等同于错误,网络源还可能只是暂时没有发完;只有确认上游输入真正结束后,才能报告截断。第三,unused_data 只是当前解压器没有消费的尾部,不代表可以继续交给同一个对象处理。若业务确实支持多个独立压缩流,应在上层明确切换到新的解压器,而不是隐式拼接。

生产代码还应保留上游的读取边界:把每个块原样交给解压器,不要因为块大小变化而改变状态判断。这样无论输入来自短读的文件、固定大小的网络包,还是消息队列中的不定长消息,循环都只由 needs_input、eof 和 unused_data 驱动。

相关问题

BZ2Decompressor 能直接处理多个压缩流吗?不能透明处理。单个对象到达 EOF 后应结束,多个独立流需要由上层识别边界并为后续流创建新的解压器。

为什么调用 decompress(b"") 还能得到输出?因为前一次调用可能只返回了 max_length 允许的部分,剩余明文已经在对象内部缓冲,空输入只是请求继续排出它。

输入结束但 eof 还是 False 怎么办?把它当成不完整压缩流处理:等待更多数据,或者在确认上游结束后抛出截断错误,不要悄悄返回半段明文。

记住一句话:needs_input 决定“要不要拿新输入”,eof 决定“当前流是否结束”,unused_data 决定“结束标记后的字节交给谁”。把这三者分开,增量解压循环就不会被输入块大小牵着走。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>