压缩编解码器在Python中是如何工作的?
我正在使用Python查询数据库并存档结果,并且在将数据写入日志文件时尝试压缩数据。不过我有点问题 我的代码如下所示:压缩编解码器在Python中是如何工作的?,python,gzip,python-2.x,bzip2,Python,Gzip,Python 2.x,Bzip2,我正在使用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)) 但是,我的输出文件的大小为1409780。在该文件上运行bunzip2
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))
但是,我的输出文件的大小为1409780。在该文件上运行bunzip2
会生成一个大小为943634的文件,在该文件上运行bzip2
会生成一个大小为217275的文件。换句话说,未压缩的文件比使用Python的bzip编解码器压缩的文件小得多除了在命令行上运行bzip2
之外,还有其他方法可以解决此问题吗?
我尝试了Python的gzip编解码器(将行更改为codecs.open(archive_file,'a+','zip')
),看看它是否解决了这个问题。我仍然得到大文件,但是当我试图解压缩文件时,我也得到了一个gzip:archive\u file:not in gzip格式的错误那里发生了什么事?
编辑:我最初是在附加模式下打开文件的,而不是在写入模式下。虽然这可能是问题,也可能不是问题,但如果文件是以“w”模式打开的,问题仍然存在 问题是由于使用了附加模式,导致文件包含多个压缩数据块。看看这个例子:
>>> 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'
这里发生的是解压需要一个压缩流。您必须检查规范,以了解多个连接流的官方行为,但根据我的经验,它们处理第一个流,而忽略其余数据。Python就是这样做的
我想bunzip2也在做同样的事情。因此,实际上您的文件是压缩的,并且比它包含的数据小得多。但是当您通过bunzip2运行它时,您只会返回您写入它的第一组记录;其余部分被丢弃。我不确定这与编解码器的方式有多大区别,但如果您使用gzip模块中的GzipFile,您可以增量附加到该文件,但除非您一次写入大量数据(可能大于1KB),否则压缩效果不会很好。这就是压缩算法的本质。如果您正在写入的数据不是非常重要(即,如果进程死亡,您可以处理丢失数据的问题),那么您可以编写一个缓冲GzipFile类,该类包装导入的类,以写出更大的数据块。问题似乎是在每个write()
上都写入了输出。这会导致每一行在其自己的bzip块中被压缩
在将其写入文件之前,我会尝试在内存中构建一个更大的字符串(如果您担心性能的话,也可以创建一个字符串列表)。一个好的拍摄尺寸应该是900K(或更大),因为这是bzip2使用的块大小正如其他海报所指出的,问题是库没有使用增量编码器来编码数据;相反,它将馈送到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,请坚持每个文件使用一个流。为什么要打开文件进行附加?这会逐渐从数据库中修剪记录并将其保存到存档文件中,因此存档文件会逐渐增长,直到从机器上复制下来。首先,大小差异是运行一次程序的结果。使用“w”运行它会生成与“a+”完全相同的文件,比未压缩版本大30%左右。其次,即使Python不读取第一个压缩数据块,但“bunzip2”会读取。