8

我正在查询数据库并使用 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”模式打开,问题仍然存在。

4

4 回答 4

2

正如其他海报所指出的那样,问题在于该codecs库不使用增量编码器来对数据进行编码。相反,它将提供给该write方法的每个数据片段编码为压缩块。这是非常低效的,对于设计用于流的库来说,这只是一个糟糕的设计决策。

具有讽刺意味的是,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 本身无法处理它(尽管有一个补丁)。如果您需要将创建的压缩文件读回 Python,请坚持每个文件使用一个流。

于 2010-09-30T14:46:07.910 回答
1

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

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

于 2010-09-29T21:28:11.907 回答
0

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

>>> 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 运行它时,你只得到你写给它的第一组记录;其余的被丢弃。

于 2010-09-29T19:17:52.487 回答
0

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

于 2010-09-29T21:18:20.903 回答