¿Cómo leer un archivo de texto comprimido con 7z?

Me gustaría leer (en Python 2.7), línea por línea, desde un archivo csv (texto), que está comprimido en 7z. No quiero descomprimir el archivo completo (grande), sino transmitir las líneas.

Intenté pylzma.decompressobj() sin éxito. Me sale un error de datos. Tenga en cuenta que este código aún no se lee línea por línea:

 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() 

Salida:

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

Esto te permitirá iterar las líneas. Se deriva parcialmente de algún código que encontré en una respuesta a otra pregunta.

Por lo que puedo decir, en este momento, py7zlib no proporciona una API que permita que los miembros del archivo se lean como una secuencia de bytes o caracteres; su clase ArchiveFile solo proporciona una función read() que descomprime y devuelve Todos los datos sin comprimir a la vez que componían el miembro. Dado que, lo mejor que puedes hacer es devolver bytes o líneas de forma iterativa, utilizando eso como un búfer. Lo siguiente hace eso, pero muchos no ayudan si el problema es que el archivo del miembro del archivo en sí es enorme.

He modificado el siguiente código para que funcione tanto en Python 2.7 como en 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 

Uso de la muestra:

 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)) 

Si estaba usando Python 3.3+, podría hacerlo usando el módulo lzma que se agregó a la biblioteca estándar en esa versión.

Ver: lzma Ejemplos