我正在尝试找出使用Python zlib
压缩流的最佳方法。
我有一个类似文件的输入流(下面是input
)和一个接受类文件(output_function
的输出函数):
with open("file") as input:
output_function(input)
我希望在将input
发送给output_function
之前先压缩with open("file") as input:
output_function(gzip_stream(input))
块:
zlib.Compress.compress
看起来gzip模块假定输入或输出都是gzip的磁盘文件......所以我假设zlib模块就是我想要的。< / p>
但是,它本身并没有提供一种简单的方法来创建类似于文件的流...而且它支持的流压缩是通过手动将数据添加到压缩缓冲区,然后刷新缓冲区来实现的。
当然,我可以在zlib.Compress.flush
和Compress
之间编写一个包装器(zlib.compressobj()
返回output_function(StringIO(zlib.compress(input.read())))
),但我会担心缓冲区大小错误或类似的东西。
那么,使用Python创建流式,gzip压缩文件的最简单方法是什么?
编辑:为了澄清,输入流和压缩输出流都太大而无法容纳在内存中,因此像{{1}}这样的东西并没有真正解决问题。
答案 0 :(得分:10)
它非常笨拙(自我引用等;只需花几分钟写一下,没有什么真正优雅的),但如果您仍然对使用gzip
而不是{{1}感兴趣的话,它会做您想要的直接。
基本上,zlib
是一个(非常有限的)类文件对象,它从给定的可迭代中生成一个gzip压缩文件(例如,类文件对象,字符串列表,任何生成器...)
当然,它产生二进制,因此实现“readline”没有任何意义。
您应该能够将其展开以涵盖其他情况或将其用作可迭代对象本身。
GzipWrap
答案 1 :(得分:7)
这是一个基于RicardoCárdenes非常有用的答案的更清洁,非自我引用的版本。
from gzip import GzipFile
from collections import deque
CHUNK = 16 * 1024
class Buffer (object):
def __init__ (self):
self.__buf = deque()
self.__size = 0
def __len__ (self):
return self.__size
def write (self, data):
self.__buf.append(data)
self.__size += len(data)
def read (self, size=-1):
if size < 0: size = self.__size
ret_list = []
while size > 0 and len(self.__buf):
s = self.__buf.popleft()
size -= len(s)
ret_list.append(s)
if size < 0:
ret_list[-1], remainder = ret_list[-1][:size], ret_list[-1][size:]
self.__buf.appendleft(remainder)
ret = ''.join(ret_list)
self.__size -= len(ret)
return ret
def flush (self):
pass
def close (self):
pass
class GzipCompressReadStream (object):
def __init__ (self, fileobj):
self.__input = fileobj
self.__buf = Buffer()
self.__gzip = GzipFile(None, mode='wb', fileobj=self.__buf)
def read (self, size=-1):
while size < 0 or len(self.__buf) < size:
s = self.__input.read(CHUNK)
if not s:
self.__gzip.close()
break
self.__gzip.write(s)
return self.__buf.read(size)
优点:
答案 2 :(得分:4)
gzip模块支持压缩到类文件对象,将fileobj参数传递给GzipFile,以及文件名。您传入的文件名不需要存在,但gzip标头有一个需要填写的文件名字段。
<强>更新强>
这个答案不起作用。例如:
# tmp/try-gzip.py
import sys
import gzip
fd=gzip.GzipFile(fileobj=sys.stdin)
sys.stdout.write(fd.read())
输出:
===> cat .bash_history | python tmp/try-gzip.py > tmp/history.gzip
Traceback (most recent call last):
File "tmp/try-gzip.py", line 7, in <module>
sys.stdout.write(fd.read())
File "/usr/lib/python2.7/gzip.py", line 254, in read
self._read(readsize)
File "/usr/lib/python2.7/gzip.py", line 288, in _read
pos = self.fileobj.tell() # Save current position
IOError: [Errno 29] Illegal seek
答案 3 :(得分:2)
将cStringIO(或StringIO)模块与zlib结合使用:
>>> import zlib
>>> from cStringIO import StringIO
>>> s.write(zlib.compress("I'm a lumberjack"))
>>> s.seek(0)
>>> zlib.decompress(s.read())
"I'm a lumberjack"
答案 4 :(得分:1)
这有效(至少在python 3中有效):
with s3.open(path, 'wb') as f:
gz = gzip.GzipFile(filename, 'wb', 9, f)
gz.write(b'hello')
gz.flush()
gz.close()
此处将其写入带有gzip压缩的s3fs的文件对象。
魔术是f
参数,它是GzipFile的fileobj
。您必须提供gzip标头的文件名。