【问题标题】:How do the compression codecs work in Python?压缩编解码器如何在 Python 中工作?
【发布时间】:2010-09-29 17:24:56
【问题描述】:

我正在查询数据库并使用 Python 归档结果,并且我正在尝试在将数据写入日志文件时对其进行压缩。不过,我遇到了一些问题。

我的代码如下所示:

log_file = codecs.open(archive_file, 'w', 'bz2')
for id, f1, f2, f3 in cursor:
    log_file.write('%s %s %s %s\n' % (id, f1 or 'NULL', f2 or 'NULL', f3))

但是,我的输出文件大小为 1,409,780。对文件运行 bunzip2 会生成大小为 943,634 的文件,运行 bzip2 会生成大小为 217,275 的文件。换句话说,未压缩的文件比使用 Python 的 bzip 编解码器压缩的文件要小得多。 除了在命令行上运行bzip2 之外,还有其他方法可以解决这个问题吗?

我尝试了 Python 的 gzip 编解码器(将行更改为 codecs.open(archive_file, 'a+', 'zip')),看看它是否解决了问题。我仍然会收到大文件,但当我尝试解压缩文件时也会收到gzip: archive_file: not in gzip format 错误。 那里发生了什么?


编辑:我最初以追加模式打开文件,而不是写入模式。虽然这可能是也可能不是问题,但如果文件以“w”模式打开,问题仍然存在。

【问题讨论】:

  • 为什么要打开文件进行追加?
  • 这会逐渐从数据库中修剪记录并将它们保存到存档文件中,因此存档文件会逐渐增长,直到从机器本身复制出来。

标签: python gzip python-2.x bzip2


【解决方案1】:

正如其他发帖者所指出的,问题在于codecs 库不使用增量编码器来编码数据;相反,它将馈送到write 方法的每个sn-p 数据编码为压缩块。这是非常低效的,而且对于设计用于处理流的库来说是一个糟糕的设计决策。

具有讽刺意味的是,Python 中已经内置了一个完全合理的增量 bz2 编码器。创建一个自动执行正确操作的“类文件”类并不难。

import bz2

class BZ2StreamEncoder(object):
    def __init__(self, filename, mode):
        self.log_file = open(filename, mode)
        self.encoder = bz2.BZ2Compressor()

    def write(self, data):
        self.log_file.write(self.encoder.compress(data))

    def flush(self):
        self.log_file.write(self.encoder.flush())
        self.log_file.flush()

    def close(self):
        self.flush()
        self.log_file.close()

log_file = BZ2StreamEncoder(archive_file, 'ab')

警告:在本例中,我以追加模式打开文件;将多个压缩流附加到单个文件与bunzip2 配合得非常好,但Python 本身无法处理它(尽管有is a patch 用于它)。如果您需要将创建的压缩文件读回 Python,请坚持每个文件使用一个流。

【讨论】:

    【解决方案2】:

    问题似乎是每个write() 上都写入了输出。这会导致每一行都被压缩到自己的 bzip 块中。

    在将其写入文件之前,我会尝试在内存中构建一个更大的字符串(或字符串列表,如果您担心性能)。一个好的拍摄大小是 900K(或更多),因为这是 bzip2 使用的块大小

    【讨论】:

      【解决方案3】:

      问题是由于您使用了附加模式,这导致文件包含多个压缩数据块。看这个例子:

      >>> import codecs
      >>> with codecs.open("myfile.zip", "a+", "zip") as f:
      >>>     f.write("ABCD")
      

      在我的系统上,这会生成一个 12 字节大小的文件。让我们看看它包含什么:

      >>> with codecs.open("myfile.zip", "r", "zip") as f:
      >>>     f.read()
      'ABCD'
      

      好的,现在让我们以追加模式进行另一次写入:

      >>> with codecs.open("myfile.zip", "a+", "zip") as f:
      >>>     f.write("EFGH")
      

      文件现在大小为 24 字节,其内容为:

      >>> with codecs.open("myfile.zip", "r", "zip") as f:
      >>>     f.read()
      'ABCD'
      

      这里发生的情况是 unzip 需要一个压缩流。您必须检查规范以查看多个级联流的官方行为,但根据我的经验,他们处理第一个并忽略其余数据。这就是 Python 所做的。

      我希望 bunzip2 也在做同样的事情。所以实际上你的文件是压缩的,并且比它包含的数据小得多。但是当你通过 bunzip2 运行它时,你只得到你写给它的第一组记录;其余的被丢弃。

      【讨论】:

      • 首先,大小差异是程序运行一次的结果。使用 'w' 运行它会产生与 'a+' 完全相同的文件,比未压缩版本大 30% 左右。其次,即使 Python 不会读取第一个压缩数据块,“bunzip2”也会。
      【解决方案4】:

      我不确定这与编解码器的执行方式有何不同,但如果您使用 gzip 模块中的 GzipFile,您可以增量附加到文件,但除非您编写大量的文件,否则压缩效果不会很好一次数据(可能 > 1 KB)。这只是压缩算法的本质。如果您正在写入的数据不是非常重要(即,如果您的进程死亡,您可以处理丢失它),那么您可以编写一个缓冲的 GzipFile 类来包装导入的类,该类会写出更大的数据块。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-30
        • 1970-01-01
        • 2017-04-10
        • 2016-12-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多