我正在查询数据库并使用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'模式打开,问题仍然存在。
答案 0 :(得分:2)
正如其他海报所指出的那样,问题是codecs
库不使用增量编码器来编码数据;相反,它将每个馈送到write
方法的数据片段编码为压缩块。这非常低效,对于设计用于流的库而言,这只是一个糟糕的设计决策。
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,请坚持每个文件使用一个流。
答案 1 :(得分:1)
问题似乎是每个write()
都会写出输出。这会导致每行在其自己的bzip块中进行压缩。
在将其写入文件之前,我会尝试在内存中构建一个更大的字符串(或者如果你担心性能的字符串列表)。拍摄的大小将是900K(或更多),因为这是bzip2使用的块大小
答案 2 :(得分: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运行它时,你只会回到你写给它的第一组记录;其余的都被丢弃了。
答案 3 :(得分:0)
我不确定这与编解码器的方式有多么不同但是如果你从gzip模块使用GzipFile,你可以逐步追加到文件但是它不会很好地压缩,除非你写的是大量的一次数据(可能> 1 KB)。这只是压缩算法的本质。如果您正在编写的数据不是非常重要(例如,如果您的进程死亡,您可以处理丢失它),那么您可以编写一个缓冲的GzipFile类来包装导出的类,该类会写出更大的数据块。