2013-11-20 2 views
12

Я хотел бы прочитать (в Python 2.7) строку за строкой из файла csv (text), который сжат 7z. Я не хочу распаковывать весь (большой) файл, но для потоковой передачи строк.Как читать из текстового файла, сжатого с помощью 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 
+2

Почему бы вам не разместить свой код и образец файла, чтобы мы могли воспроизвести вашу ошибку и можно увидеть, как мы можем помочь? –

+0

.7z файл - это контейнеры (архивы), которые могут содержать более одного файла, так что же имя файла внутри 'testing.7z' вы хотите прочитать? – martineau

+0

@martineau, testing.csv – Yariv

ответ

7

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

Насколько я могу сказать, в этот момент времени 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 добавлен в стандартную библиотеку в этой версии.

См: lzmaExamples

+2

Вопрос помечен 'python-2.7', поэтому мы можем предположить, что это * не * Python 3 здесь. –

+0

Также вы должны упомянуть python 3.3 (из ссылки doc) и не только 3. –

+1

@MartijnPieters не имеет этого тега, когда я прокомментировал. – blakev

Смежные вопросы