我试图找出用Python压缩流的最佳方法zlib.
我有一个类似文件的输入流(input,下面)和一个输出函数,它接受类似文件(output_function,下面):
with open("file") as input:
output_function(input)
Run Code Online (Sandbox Code Playgroud)
我想input在发送之前对gzip压缩块进行压缩output_function:
with open("file") as input:
output_function(gzip_stream(input))
Run Code Online (Sandbox Code Playgroud)
看起来gzip模块假定输入或输出都是gzip的磁盘文件...所以我假设zlib模块是我想要的.
但是,它本身并没有提供一种简单的方法来创建类似于文件的流......而它支持的流压缩是通过手动将数据添加到压缩缓冲区,然后刷新缓冲区来实现的.
当然,我可以写一个包装器zlib.Compress.compress和zlib.Compress.flush(Compress由它返回zlib.compressobj()),但我担心缓冲区大小错误或类似的东西.
那么,使用Python创建流式,gzip压缩文件的最简单方法是什么?
编辑:为了澄清,输入流和压缩输出流都太大output_function(StringIO(zlib.compress(input.read())))而不适合内存,所以类似的东西并没有真正解决问题.
Ric*_*nes 11
它非常笨拙(自我引用等;只需花几分钟写一下,没有什么真正的优雅),但是如果你仍然有兴趣使用gzip而不是zlib直接使用它,它会做你想要的.
基本上,GzipWrap是一个(非常有限的)类文件对象,它从给定的可迭代中生成一个gzip压缩文件(例如,类文件对象,字符串列表,任何生成器...)
当然,它产生二进制因此在实现"readline"时没有任何意义.
您应该能够将其展开以涵盖其他情况或将其用作可迭代对象本身.
from gzip import GzipFile
class GzipWrap(object):
# input is a filelike object that feeds the input
def __init__(self, input, filename = None):
self.input = input
self.buffer = ''
self.zipper = GzipFile(filename, mode = 'wb', fileobj = self)
def read(self, size=-1):
if (size < 0) or len(self.buffer) < size:
for s in self.input:
self.zipper.write(s)
if size > 0 and len(self.buffer) >= size:
self.zipper.flush()
break
else:
self.zipper.close()
if size < 0:
ret = self.buffer
self.buffer = ''
else:
ret, self.buffer = self.buffer[:size], self.buffer[size:]
return ret
def flush(self):
pass
def write(self, data):
self.buffer += data
def close(self):
self.input.close()
Run Code Online (Sandbox Code Playgroud)
这是一个更清洁,非自我参考的版本,基于里卡多Cá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)
Run Code Online (Sandbox Code Playgroud)
好处:
小智 5
gzip 模块支持压缩为类文件对象,将 fileobj 参数以及文件名传递给 GzipFile。您传入的文件名不需要存在,但 gzip 标头有一个需要填写的文件名字段。
更新
这个答案不起作用。例子:
# tmp/try-gzip.py
import sys
import gzip
fd=gzip.GzipFile(fileobj=sys.stdin)
sys.stdout.write(fd.read())
Run Code Online (Sandbox Code Playgroud)
输出:
===> 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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
13617 次 |
| 最近记录: |