Я пытаюсь выяснить лучший способ сжать поток с Python zlib
.
У меня есть подобный файлу входной поток (input
, ниже) и функция вывода, которая принимает подобное файлу (output_function
, ниже):
with open("file") as input:
output_function(input)
И я хотел бы gzip-сжаться input
блоки прежде, чем отправить их в output_function
:
with open("file") as input:
output_function(gzip_stream(input))
Похоже, что gzip модуль предполагает, что или вход или вывод будут gzip'd файлом на диске …, Таким образом, я предположу, что zlib модуль - то, что я хочу.
Однако это исходно не предлагает простой способ создать поток подобный файлу … И потоковое сжатие, которое это действительно поддерживает, прибывает посредством вручную добавляющих данных в буфер сжатия, затем сбрасывая тот буфер.
Конечно, я мог записать обертку вокруг zlib.Compress.compress
и zlib.Compress.flush
(Compress
возвращается zlib.compressobj()
), но я был бы взволнован по поводу понимания превратно буферных размеров или чего-то подобного.
Так, что самый простой путь состоит в том, чтобы создать потоковую передачу, gzip-сжав подобный файлу с Python?
Править: Для разъяснения входной поток и сжатый поток вывода являются оба слишком большими для умещений в памяти, таким образом, что-то как output_function(StringIO(zlib.compress(input.read())))
действительно не решает проблему.
Это довольно громоздко (самореклама и т.д. несколько минут на написание, ничего особенного), но он делает то, что вам нужно, если вы все еще заинтересованы в использовании gzip
вместо zlib
напрямую.
По сути, GzipWrap
- это (очень ограниченный) файловый объект, который создает сжатый файл из заданного итеративного объекта (например, файлового объекта, списка строк, любого генератора .. .)
Конечно, он производит двоичный файл, поэтому не было смысла реализовывать "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()
Модуль 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
Используйте модуль 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"