compression.zstd --- 与 Zstandard 格式兼容的压缩

在 3.14 版本加入。

源代码: Lib/compression/zstd/__init__.py


此模块提供了使用 Zstandard(或 zstd)压缩算法来压缩和解压缩数据的类和函数。zstd 手册将 Zstandard 描述为“一种快速的无损压缩算法,旨在实现 zlib 级别的实时压缩场景和更好的压缩率。”模块还包含一个文件接口,支持读写由 zstd 工具创建的 .zst 文件内容,以及原始的 zstd 压缩流。

compression.zstd 模块包含:

异常

exception compression.zstd.ZstdError

当压缩或解压缩过程中,或在初始化(解)压缩器状态时发生错误,会引发此异常。

读写压缩文件

compression.zstd.open(file, /, mode='rb', *, level=None, options=None, zstd_dict=None, encoding=None, errors=None, newline=None)

以二进制或文本模式打开一个 Zstandard 压缩文件,返回一个 文件对象

file 参数可以是一个文件名(以 strbytes类路径 对象的形式给出),在这种情况下,将打开指定的文件;或者它也可以是一个已存在的文件对象,用于读取或写入。

mode 参数可以是用于读取的 'rb'(默认)、用于覆盖写入的 'wb'、用于追加的 'ab',或用于独占创建的 'xb'。它们也可以分别等价地表示为 'r''w''a''x'。你也可以分别使用 'rt''wt''at''xt' 以文本模式打开。

读取时,options 参数可以是一个提供高级解压缩参数的字典;有关支持的参数的详细信息,请参见 DecompressionParameterzstd_dict 参数是一个 ZstdDict 实例,用于解压缩过程。读取时,如果 level 参数不为 None,将引发 TypeError

写入时,options 参数可以是一个提供高级压缩参数的字典;有关支持的参数的详细信息,请参见 CompressionParameterlevel 参数是写入压缩数据时使用的压缩级别。leveloptions 中只能有一个不为 None。zstd_dict 参数是一个 ZstdDict 实例,用于压缩过程。

在二进制模式下,此函数等同于 ZstdFile 的构造函数:ZstdFile(file, mode, ...)。在这种情况下,不得提供 encodingerrorsnewline 参数。

在文本模式下,会创建一个 ZstdFile 对象,并将其包装在一个 io.TextIOWrapper 实例中,该实例具有指定的编码、错误处理行为和行尾符。

class compression.zstd.ZstdFile(file, /, mode='rb', *, level=None, options=None, zstd_dict=None)

以二进制模式打开一个 Zstandard 压缩文件。

一个 ZstdFile 可以包装一个已经打开的 文件对象,或者直接操作一个指定名称的文件。file 参数指定要包装的文件对象,或者要打开的文件名(作为 strbytes类路径 对象)。如果包装一个已存在的文件对象,当 ZstdFile 关闭时,被包装的文件不会被关闭。

mode 参数可以是用于读取的 'rb'(默认)、用于覆盖写入的 'wb'、用于独占创建的 'xb',或用于追加的 'ab'。它们也可以分别等价地表示为 'r''w''x''a'

如果 file 是一个文件对象(而不是实际的文件名),模式 'w' 不会截断文件,而是等同于 'a'

读取时,options 参数可以是一个提供高级解压缩参数的字典;有关支持的参数的详细信息,请参见 DecompressionParameterzstd_dict 参数是一个 ZstdDict 实例,用于解压缩过程。读取时,如果 level 参数不为 None,将引发 TypeError

写入时,options 参数可以是一个提供高级压缩参数的字典;有关支持的参数的详细信息,请参见 CompressionParameterlevel 参数是写入压缩数据时使用的压缩级别。leveloptions 中只能有一个被传入。zstd_dict 参数是一个 ZstdDict 实例,用于压缩过程。

ZstdFile 支持 io.BufferedIOBase 指定的所有成员,除了 detach()truncate()。支持迭代和 with 语句。

还提供以下方法和属性:

peek(size=-1)

返回缓冲的数据而不移动文件位置。除非已到达文件末尾(EOF),否则将至少返回一个字节的数据。返回的确切字节数是未指定的(size 参数被忽略)。

备注

虽然调用 peek() 不会改变 ZstdFile 的文件位置,但它可能会改变底层文件对象的位置(例如,如果 ZstdFile 是通过为 file 传递文件对象来构造的)。

mode

读取时为 'rb',写入时为 'wb'

name

Zstandard 文件的名称。等同于底层 文件对象name 属性。

在内存中压缩和解压缩数据

compression.zstd.compress(data, level=None, options=None, zstd_dict=None)

压缩 data(一个 类字节对象),返回压缩后的数据,为一个 bytes 对象。

level 参数是一个控制压缩级别的整数。level 是在 options 中设置 CompressionParameter.compression_level 的替代方法。在 compression_level 上使用 bounds() 来获取可以为 level 传递的值。如果需要高级压缩选项,必须省略 level 参数,并在 options 字典中设置 CompressionParameter.compression_level 参数。

options 参数是一个包含高级压缩参数的 Python 字典。压缩参数的有效键和值在 CompressionParameter 文档中有详细说明。

zstd_dict 参数是 ZstdDict 的一个实例,包含用于提高压缩效率的训练数据。可以使用 train_dict() 函数生成 Zstandard 字典。

compression.zstd.decompress(data, zstd_dict=None, options=None)

解压缩 data(一个 类字节对象),返回解压缩后的数据,为一个 bytes 对象。

options 参数是一个包含高级解压缩参数的 Python 字典。压缩参数的有效键和值在 DecompressionParameter 文档中有详细说明。

zstd_dict 参数是 ZstdDict 的一个实例,包含在压缩过程中使用的训练数据。这必须是压缩时使用的同一个 Zstandard 字典。

如果 data 是多个不同压缩帧的拼接,则解压缩所有这些帧,并返回结果的拼接。

class compression.zstd.ZstdCompressor(level=None, options=None, zstd_dict=None)

创建一个压缩器对象,可用于增量压缩数据。

要以更方便的方式压缩单个数据块,请参阅模块级函数 compress()

level 参数是一个控制压缩级别的整数。level 是在 options 中设置 CompressionParameter.compression_level 的替代方法。在 compression_level 上使用 bounds() 来获取可以为 level 传递的值。如果需要高级压缩选项,必须省略 level 参数,并在 options 字典中设置 CompressionParameter.compression_level 参数。

options 参数是一个包含高级压缩参数的 Python 字典。压缩参数的有效键和值在 CompressionParameter 文档中有详细说明。

zstd_dict 参数是 ZstdDict 的一个可选实例,包含用于提高压缩效率的训练数据。可以使用 train_dict() 函数生成 Zstandard 字典。

compress(data, mode=ZstdCompressor.CONTINUE)

压缩 data(一个 类字节对象),如果可能,返回一个包含压缩数据的 bytes 对象,否则返回一个空的 bytes 对象。部分 data 可能会被内部缓冲,以供后续调用 compress()flush() 时使用。返回的数据应与之前任何 compress() 调用的输出拼接起来。

mode 参数是一个 ZstdCompressor 属性,可以是 CONTINUEFLUSH_BLOCKFLUSH_FRAME

当所有数据都提供给压缩器后,调用 flush() 方法来完成压缩过程。如果以 mode 设置为 FLUSH_FRAME 调用 compress(),则不应调用 flush(),因为它会写入一个新的空帧。

flush(mode=ZstdCompressor.FLUSH_FRAME)

完成压缩过程,返回一个 bytes 对象,其中包含存储在压缩器内部缓冲区中的任何数据。

mode 参数是一个 ZstdCompressor 属性,可以是 FLUSH_BLOCKFLUSH_FRAME

set_pledged_input_size(size)

指定将为下一帧提供的未压缩数据量 sizesize 将被写入下一帧的帧头中,除非 CompressionParameter.content_size_flagFalse0。大小为 0 表示帧为空。如果 sizeNone,帧头将省略帧大小。包含未压缩数据大小的帧在解压缩时需要更少的内存,尤其是在较高压缩级别下。

如果 last_mode 不是 FLUSH_FRAME,会引发 ValueError,因为压缩器不在帧的起始位置。如果承诺的大小与提供给 compress() 的实际数据大小不匹配,未来对 compress()flush() 的调用可能会引发 ZstdError,并且最后的数据块可能会丢失。

在调用 flush()compress() 并将模式设为 FLUSH_FRAME 后,除非再次调用 set_pledged_input_size(),否则下一帧的帧头将不包含帧大小。

CONTINUE

收集更多数据进行压缩,可能不会立即生成输出。此模式通过最大化每个块和帧的数据量来优化压缩率。

FLUSH_BLOCK

完成并将一个块写入数据流。目前为止返回的数据可以立即解压缩。过去的数据仍然可以在后续调用 compress() 生成的块中被引用,从而提高压缩效果。

FLUSH_FRAME

完成并写出一个帧。后续提供给 compress() 的数据将被写入一个新帧,并且 不能 引用过去的数据。

last_mode

最后一次传递给 compress()flush() 的模式。值可以是 CONTINUEFLUSH_BLOCKFLUSH_FRAME。初始值为 FLUSH_FRAME,表示压缩器处于新帧的开始。

class compression.zstd.ZstdDecompressor(zstd_dict=None, options=None)

创建一个解压缩器对象,可用于增量解压缩数据。

要以更方便的方式一次性解压缩整个压缩流,请参阅模块级函数 decompress()

options 参数是一个包含高级解压缩参数的 Python 字典。压缩参数的有效键和值在 DecompressionParameter 文档中有详细说明。

zstd_dict 参数是 ZstdDict 的一个实例,包含在压缩过程中使用的训练数据。这必须是压缩时使用的同一个 Zstandard 字典。

备注

decompress() 函数和 ZstdFile 类不同,此类不会透明地处理包含多个压缩帧的输入。要解压缩多帧输入,应使用 decompress(),如果处理 文件对象 则使用 ZstdFile,或者使用多个 ZstdDecompressor 实例。

decompress(data, max_length=-1)

解压缩 data(一个 类字节对象),返回解压缩后的字节数据。部分 data 可能会被内部缓冲,以供后续调用 decompress() 时使用。返回的数据应与之前任何 decompress() 调用的输出拼接起来。

如果 max_length 为非负数,则该方法最多返回 max_length 字节的解压缩数据。如果达到此限制并且可以产生更多输出,needs_input 属性将设置为 False。在这种情况下,下一次调用 decompress() 时可以提供 datab'' 来获取更多输出。

如果所有输入数据都已解压缩并返回(因为其小于 max_length 字节,或者因为 max_length 为负数),则 needs_input 属性将设置为 True

试图在帧结束后解压缩数据会引发 ZstdError。在帧结束后找到的任何数据都将被忽略并保存在 unused_data 属性中。

eof

如果已到达流末尾标记,则为 True

unused_data

压缩流结束后发现的数据。

在到达流末尾之前,此属性将为 b''

needs_input

如果 decompress() 方法在需要新的压缩输入之前可以提供更多解压缩数据,则为 False

Zstandard 字典

compression.zstd.train_dict(samples, dict_size)

训练一个 Zstandard 字典,返回一个 ZstdDict 实例。Zstandard 字典可以更有效地压缩小数据,这些数据传统上由于重复性较少而难以压缩。如果你要压缩多个相似的数据组(例如相似的文件),Zstandard 字典可以显著提高压缩率和速度。

samples 参数(一个 bytes 对象的可迭代对象),是用于训练 Zstandard 字典的样本总体。

dict_size 参数是一个整数,表示 Zstandard 字典的最大尺寸(以字节为单位)。Zstandard 文档建议绝对最大值不超过 100 KB,但根据数据的不同,最大值通常可以更小。较大的字典通常会减慢压缩速度,但能提高压缩率。较小的字典压缩速度更快,但会降低压缩率。

compression.zstd.finalize_dict(zstd_dict, /, samples, dict_size, level)

一个高级函数,用于将“原始内容”Zstandard 字典转换为常规 Zstandard 字典。“原始内容”字典是一个字节序列,不需要遵循普通 Zstandard 字典的结构。

zstd_dict 参数是一个 ZstdDict 实例,其 dict_content 包含原始字典内容。

samples 参数(一个 bytes 对象的可迭代对象),包含用于生成 Zstandard 字典的样本数据。

dict_size 参数是一个整数,表示 Zstandard 字典的最大尺寸(以字节为单位)。有关最大字典大小的建议,请参见 train_dict()

level 参数(一个整数)是期望传递给使用此字典的压缩器的压缩级别。字典信息因每个压缩级别而异,因此针对适当的压缩级别进行调整可以使压缩更有效。

class compression.zstd.ZstdDict(dict_content, /, *, is_raw=False)

Zstandard 字典的包装器。字典可用于提高许多小数据块的压缩效果。如果需要从样本数据中训练新字典,请使用 train_dict()

dict_content 参数(一个 类字节对象)是已经训练好的字典信息。

is_raw 参数是一个布尔值,是一个控制 dict_content 含义的高级参数。True 表示 dict_content 是一个“原始内容”字典,没有任何格式限制。False 表示 dict_content 是一个普通的 Zstandard 字典,由 Zstandard 函数创建,例如 train_dict() 或外部 zstd 命令行工具。

当向函数传递一个 ZstdDict 时,可以通过将 as_digested_dictas_undigested_dict 属性作为 zstd_dict 参数传递来控制字典的加载方式,例如 compress(data, zstd_dict=zd.as_digested_dict)。消化字典是在加载 Zstandard 字典时发生的一项耗时操作。当多次调用压缩或解压缩时,传递一个已消化的字典将减少加载字典的开销。

压缩时的区别

已消化的字典

未消化的字典

可能会被字典参数覆盖的压缩器高级参数

window_log, hash_log, chain_log, search_log, min_match, target_length, strategy, enable_long_distance_matching, ldm_hash_log, ldm_min_match, ldm_bucket_size_log, ldm_hash_rate_log 以及一些非公开参数。

None

ZstdDict 内部缓存字典

是。当再次使用相同压缩级别加载已消化的字典时速度更快。

否。如果你希望多次加载一个未消化的字典,请考虑重用一个压缩器对象。

如果传递一个不带任何属性的 ZstdDict,压缩时默认传递一个未消化的字典,解压缩时如果需要则会生成并默认传递一个已消化的字典。

dict_content

Zstandard 字典的内容,一个 bytes 对象。它与 __init__ 方法中的 dict_content 参数相同。它可以与其他程序一起使用,例如 zstd 命令行程序。

dict_id

Zstandard 字典的标识符,一个非负整数值。

非零值表示该字典是普通的,由 Zstandard 函数创建并遵循 Zstandard 格式。

0 表示一个“原始内容”字典,没有任何格式限制,供高级用户使用。

备注

ZstdDict.dict_id0 的含义不同于 get_frame_info() 函数的 dictionary_id 属性。

as_digested_dict

作为已消化的字典加载。

as_undigested_dict

作为未消化的字典加载。

高级参数控制

class compression.zstd.CompressionParameter

一个 IntEnum,包含可在压缩数据时使用的高级压缩参数键。

可以在任何属性上使用 bounds() 方法来获取该参数的有效值。

参数是可选的;任何省略的参数将自动选择其值。

获取 compression_level 上下限的示例:

lower, upper = CompressionParameter.compression_level.bounds()

window_log 设置为最大尺寸的示例:

_lower, upper = CompressionParameter.window_log.bounds()
options = {CompressionParameter.window_log: upper}
compress(b'venezuelan beaver cheese', options=options)
bounds()

返回一个压缩参数的整数边界元组 (lower, upper)。此方法应在你希望检索其边界的属性上调用。例如,要获取 compression_level 的有效值,可以检查 CompressionParameter.compression_level.bounds() 的结果。

上下限都是包含性的。

compression_level

一种高级方式,用于设置影响数据压缩速度和压缩率的其他压缩参数。

常规压缩级别大于 0。大于 20 的值被认为是“超”压缩,并且比其他级别需要更多内存。负值可用于以较差的压缩率换取更快的压缩速度。

将级别设置为零将使用 COMPRESSION_LEVEL_DEFAULT

window_log

压缩器在压缩数据时可以使用的最大允许反向引用距离,表示为2的幂,即 1 << window_log 字节。此参数极大地影响压缩的内存使用。更高的值需要更多内存,但能获得更好的压缩值。

值为零会导致该值被自动选择。

hash_log

初始探测表的大小,表示为2的幂。最终的内存使用量为 1 << (hash_log+2) 字节。对于 <= dfast 的策略,较大的表能提高压缩率;对于 > dfast 的策略,能提高压缩速度。

值为零会导致该值被自动选择。

chain_log

多探测搜索表的大小,表示为2的幂。最终的内存使用量为 1 << (chain_log+2) 字节。较大的表能带来更好但更慢的压缩效果。此参数对 fast 策略无效。在使用 dfast 策略时仍然有用,此时它定义了一个次级探测表。

值为零会导致该值被自动选择。

search_log

搜索尝试次数,表示为2的幂。更多的尝试会带来更好但更慢的压缩效果。此参数对 fastdfast 策略无用。

值为零会导致该值被自动选择。

min_match

搜索匹配的最小尺寸。较大的值会提高压缩和解压缩速度,但会降低压缩率。请注意,Zstandard 仍然可以找到更小尺寸的匹配,它只是调整其搜索算法以寻找此尺寸及更大的匹配。对于所有 < btopt 的策略,有效最小值为 4;对于所有 > fast 的策略,有效最大值为 6

值为零会导致该值被自动选择。

target_length

此字段的影响取决于所选的 Strategy

对于策略 btoptbtultrabtultra2,该值是被认为“足够好”以停止搜索的匹配长度。较大的值会使压缩率更好,但压缩速度更慢。

对于策略 fast,它是匹配采样的距离。较大的值会使压缩更快,但压缩率更差。

值为零会导致该值被自动选择。

strategy

所选策略的值越高,zstd 使用的压缩技术就越复杂,从而导致更高的压缩率但更慢的压缩速度。

参见

策略

enable_long_distance_matching

长距离匹配可用于通过在更远距离处找到大型匹配来提高大输入的压缩效果。它会增加内存使用量和窗口大小。

True1 启用长距离匹配,而 False0 禁用它。

启用此参数会增加默认的 window_log 至 128 MiB,除非明确设置为不同值。如果 window_log >= 128 MiB 且压缩策略 >= btopt(压缩级别 16+),则默认启用此设置。

ldm_hash_log

长距离匹配表的大小,表示为2的幂。较大的值会增加内存使用量和压缩率,但会降低压缩速度。

值为零会导致该值被自动选择。

ldm_min_match

长距离匹配器的最小匹配尺寸。过大或过小的值通常会降低压缩率。

值为零会导致该值被自动选择。

ldm_bucket_size_log

长距离匹配器(long distance matcher)哈希表中用于解决冲突的每个桶(bucket)大小的对数值。值越大,冲突解决能力越强,但会降低压缩速度。

值为零会导致该值被自动选择。

ldm_hash_rate_log

向长距离匹配器哈希表中插入/查找条目的频率。值越大,压缩速度越快。与默认值偏离过大可能会导致压缩率下降。

值为零会导致该值被自动选择。

content_size_flag

在压缩前已知待压缩数据大小时,将其写入 Zstandard 帧头。

此标志仅在以下情况下生效:

所有其他压缩调用可能不会将大小信息写入帧头。

True1 启用内容大小标志,而 False0 则禁用它。

checksum_flag

在每个帧的末尾写入一个四字节的校验和,该校验和使用 XXHash64 算法计算未压缩内容的哈希值。Zstandard 的解压代码会验证此校验和。如果不匹配,则会引发 ZstdError 异常。

True1 启用校验和生成,而 False0 则禁用它。

dict_id_flag

使用 ZstdDict 进行压缩时,会将字典的 ID 写入帧头。

True1 启用存储字典 ID,而 False0 则禁用它。

nb_workers

选择要生成的并行压缩线程数。当 nb_workers > 0 时,启用多线程压缩。值为 1 表示“单线程的多线程模式”。更多的线程可以提高速度,但也会增加内存使用量并轻微降低压缩率。

值为零则禁用多线程。

job_size

一个压缩作业的大小,以字节为单位。该值仅在 nb_workers >= 1 时强制执行。每个压缩作业都是并行完成的,因此该值可以间接影响活动线程的数量。

值为零会导致该值被自动选择。

overlap_log

设置从先前作业(线程)中重新加载多少数据,以供新作业在压缩期间通过后向查找窗口(look behind window)使用。此值仅在 nb_workers >= 1 时使用。可接受的值范围为 0 到 9。

  • 0 表示动态设置重叠量

  • 1 表示不重叠

  • 9 表示使用前一个作业的完整窗口大小

每增加一,重叠大小减半/加倍。“8”表示 window_size/2 的重叠,“7”表示 window_size/4 的重叠,以此类推。

class compression.zstd.DecompressionParameter

一个 IntEnum,包含解压数据时可使用的高级解压参数键。参数是可选的;任何省略的参数将自动选择其值。

可以在任何属性上使用 bounds() 方法来获取该参数的有效值范围。

window_log_max 设置为最大值的示例

data = compress(b'Some very long buffer of bytes...')

_lower, upper = DecompressionParameter.window_log_max.bounds()

options = {DecompressionParameter.window_log_max: upper}
decompress(data, options=options)
bounds()

返回一个解压参数的整数边界元组 (lower, upper)。应在你希望检索其边界的属性上调用此方法。

上下限都是包含性的。

window_log_max

解压期间使用的窗口最大尺寸的以 2 为底的对数。这对于限制解压数据时使用的内存量很有用。较大的最大窗口尺寸可带来更快的解压速度。

值为零会导致该值被自动选择。

class compression.zstd.Strategy

一个包含压缩策略的 IntEnum。编号越高的策略对应越复杂和越慢的压缩。

备注

Strategy 属性的值在不同的 zstd 版本之间不一定稳定。唯一可以依赖的是属性的顺序。下面按顺序列出了这些属性。

可用的策略如下:

fast
dfast
greedy
lazy
lazy2
btlazy2
btopt
btultra
btultra2

杂项

compression.zstd.get_frame_info(frame_buffer)

检索一个包含 Zstandard 帧元数据的 FrameInfo 对象。帧包含与其所持有的压缩数据相关的元数据。

class compression.zstd.FrameInfo

与 Zstandard 帧相关的元数据。

decompressed_size

帧解压后内容的大小。

dictionary_id

一个整数,表示解压该帧所需的 Zstandard 字典 ID。0 表示字典 ID 未记录在帧头中。这可能意味着不需要 Zstandard 字典,或者所需字典的 ID 没有被记录。

compression.zstd.COMPRESSION_LEVEL_DEFAULT

Zstandard 的默认压缩级别:3

compression.zstd.zstd_version_info

运行时 zstd 库的版本号,为一个整数元组 (主版本号, 次版本号, 修订号)。

示例

读取压缩文件

from compression import zstd

with zstd.open("file.zst") as f:
    file_content = f.read()

创建压缩文件

from compression import zstd

data = b"Insert Data Here"
with zstd.open("file.zst", "w") as f:
    f.write(data)

在内存中压缩数据

from compression import zstd

data_in = b"Insert Data Here"
data_out = zstd.compress(data_in)

增量压缩

from compression import zstd

comp = zstd.ZstdCompressor()
out1 = comp.compress(b"Some data\n")
out2 = comp.compress(b"Another piece of data\n")
out3 = comp.compress(b"Even more data\n")
out4 = comp.flush()
# Concatenate all the partial results:
result = b"".join([out1, out2, out3, out4])

将压缩数据写入已打开的文件

from compression import zstd

with open("myfile", "wb") as f:
    f.write(b"This data will not be compressed\n")
    with zstd.open(f, "w") as zstf:
        zstf.write(b"This *will* be compressed\n")
    f.write(b"Not compressed\n")

使用压缩参数创建压缩文件

from compression import zstd

options = {
   zstd.CompressionParameter.checksum_flag: 1
}
with zstd.open("file.zst", "w", options=options) as f:
    f.write(b"Mind if I squeeze in?")