我必须压缩一长串字符串。我必须单独压缩它们。每个字符串的长度少于 1000 个字符。然而,许多这些字符串都有一个共同的前缀。因此,我想知道是否可以通过首先压缩公共(public)前缀,然后存储压缩器的状态并向其提供字符串的后缀来分摊压缩成本。
如果您对如何在 Python 中完成此任务有任何建议,那就太好了。尽管我在标题中提到了 zlib,但任何其他标准模块也可以工作。在这个应用程序中,解压速度并不重要,所以我可以承受相当慢的解压速度。
最佳答案
Python interface to zlib相当微薄,并且不提供对所有 zlib's capabilities 的访问。如果您可以构建自己的 zlib 接口(interface),那么您就可以做您所要求的事情,甚至更多。
“以及更多”与您单独压缩非常短的字符串这一事实有关,这本质上限制了您可以获得的压缩程度。由于这些字符串具有一些共同的内容,因此您应该使用 zlib 的 deflateSetDictionary()
和 inflateSetDictionary()
函数来利用这一事实,并有可能显着提高压缩率。公共(public)内容可以是您提到的公共(public)前缀,也可以是字符串中其他任何位置的公共(public)内容。您可以定义一个固定字典,用于最大 32K 的所有字符串,其中包含字符串中常见的字节序列。您可以将最常见的序列放在 32K 的末尾,将不太常见的序列放在前面。如果这些字符串有多个类具有不同的公共(public)序列,您可以创建一组字典并使用第一次调用 inflate()
返回的字典 ID 来选择字典。对于一个或多个字典,只需确保压缩端和解压端存储相同的字典即可。
至于存储压缩状态,您可以使用deflateCopy()
来完成。这是在 Python 中通过 copy()
方法提供的。我不确定这会给你带来很大的速度优势,尽管对于小字符串。
更新:
根据最近添加的评论,我相信您的用例是根据请求向接收者发送许多字符串中的一些。在这种情况下,可能有一种方法可以使用简陋的 Python 接口(interface)来获得更好的压缩效果。您可以将 flush
方法与 Z_SYNC_FLUSH
结合使用,强制将目前已压缩的内容输出到输出。这将允许您将请求的一系列字符串视为单个压缩流。
这个过程是用 compressobj()
启动一个压缩对象,在该对象上使用第一个请求的字符串 compress()
,收集该对象的输出(如果有),然后对对象执行flush(Z_SYNC_FLUSH)
,收集剩余的输出。将 compress()
和 flush()
的组合输出发送到接收器,接收器已启动 decompressobj()
,然后使用 decompress()
在该对象上使用发送的内容,这将返回原始字符串。 (减压端无需冲洗。)
到目前为止,结果与压缩第一个字符串没有太大不同。好处是您可以重复该过程,而无需创建新的压缩或解压缩对象。只需对下一个字符串使用 compress()
和 flush()
,并在另一端使用 decompress()
即可获取它。第二个字符串以及所有后续字符串的优点是它们可以使用先前字符串的历史记录进行压缩。那么你就不需要构建或使用任何固定的字典。您可以仅使用先前请求的字符串的历史记录来提供良好压缩所需的素材。如果您的字符串平均长度为 1000 字节,则最终发送的每个字符串都将受益于最近发送的 32 个字符串的历史记录,因为压缩的滑动窗口为 32K 长。
完成后,只需关闭对象即可。
关于python - 如何在Python中复制zlib压缩器对象的内部状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11662745/