Как читать из текстового файла, сжатого с помощью 7z?

Я хотел бы прочитать (в Python 2.7) по строкам из файла csv (text), который сжат с 7z. Я не хочу распаковывать весь (большой) файл, но для потоковой передачи строк.

Я пробовал pylzma.decompressobj() безуспешно. Я получаю ошибку данных. Обратите внимание, что этот код еще не читается последовательно:

input_filename = r"testing.csv.7z"
with open(input_filename, 'rb') as infile:
    obj = pylzma.decompressobj()
    o = open('decompressed.raw', 'wb')
    obj = pylzma.decompressobj()
    while True:
        tmp = infile.read(1)
        if not tmp: break
        o.write(obj.decompress(tmp))
    o.close()

Вывод:

    o.write(obj.decompress(tmp))
ValueError: data error during decompression

Ответ 1

Это позволит вам итерировать строки. Он частично получен из некоторого кода, который я нашел в ответе , на другой вопрос.

Насколько я могу судить, на данный момент py7zlib не предоставляет API, который позволял бы членам архива считываться как поток байтов или символов, его класс ArchiveFile предоставляет только read(), которая распаковывает и возвращает сразу все несжатые данные, которые состоят из члена. Учитывая, что самое лучшее, что вы можете сделать, это вернуть байты или строки, итеративно используя это как буфер. Следующее делает это, но многие не помогают, если проблема в файле самого файла архива огромна.

Я изменил код ниже, чтобы работать как в Python 2.7, так и 3.x.

import io
import os
import py7zlib

class SevenZFileError(py7zlib.ArchiveError):
    pass

class SevenZFile(object):
    @classmethod
    def is_7zfile(cls, filepath):
        """ Determine if filepath points to a valid 7z archive. """
        is7z = False
        fp = None
        try:
            fp = open(filepath, 'rb')
            archive = py7zlib.Archive7z(fp)
            _ = len(archive.getnames())
            is7z = True
        finally:
            if fp: fp.close()
        return is7z

    def __init__(self, filepath):
        fp = open(filepath, 'rb')
        self.filepath = filepath
        self.archive = py7zlib.Archive7z(fp)

    def __contains__(self, name):
        return name in self.archive.getnames()

    def readlines(self, name):
        """ Iterator of lines from an archive member. """
        if name not in self:
            raise SevenZFileError('archive member %r not found in %r' %
                                  (name, self.filepath))

        for line in io.StringIO(self.archive.getmember(name).read().decode()):
            yield line

Использование образца:

import csv

if SevenZFile.is_7zfile('testing.csv.7z'):
    sevenZfile = SevenZFile('testing.csv.7z')

    if 'testing.csv' not in sevenZfile:
        print('testing.csv is not a member of testing.csv.7z')
    else:
        reader = csv.reader(sevenZfile.readlines('testing.csv'))
        for row in reader:
            print(', '.join(row))

Ответ 2

Если вы использовали Python 3.3+, вы могли бы сделать это, используя модуль lzma, который был добавлен к стандарту библиотеки в этой версии.

Смотрите: lzma Примеры