2011-04-11 18 views
20

Un sencillo programa para la lectura de un archivo CSV en un archivo zip funciona en Python 2.7, pero no en Python 3,2py3k: ¿Cómo se lee un archivo dentro de un archivo zip como texto, no como bytes?

$ cat test_zip_file_py3k.py 
import csv, sys, zipfile 

zip_file = zipfile.ZipFile(sys.argv[1]) 
items_file = zip_file.open('items.csv', 'rU') 

for row in csv.DictReader(items_file): 
    pass 

$ python2.7 test_zip_file_py3k.py ~/data.zip 

$ python3.2 test_zip_file_py3k.py ~/data.zip 
Traceback (most recent call last): 
    File "test_zip_file_py3k.py", line 8, in <module> 
    for row in csv.DictReader(items_file): 
    File "/home/msabramo/run/lib/python3.2/csv.py", line 109, in __next__ 
    self.fieldnames 
    File "/home/msabramo/run/lib/python3.2/csv.py", line 96, in fieldnames 
    self._fieldnames = next(self.reader) 
_csv.Error: iterator should return strings, not bytes (did you open the file 
in text mode?) 

Así el módulo csv en Python 3 quiere ver un archivo de texto, pero zipfile.ZipFile.open devuelve un zipfile.ZipExtFile eso siempre se trata como datos binarios.

¿Cómo funciona esto en Python 3?

Respuesta

22

Acabo de notar que Lennart's answer no funcionó con Python 3.1 , pero hace trabajo con Python 3.2. Han mejorado zipfile.ZipExtFile en Python 3.2 (ver release notes). Estos cambios parecen hacer que zipfile.ZipExtFile funcione bien con io.TextWrapper.

Dicho sea de paso, funciona en Python 3.1, si descomenta las líneas hacky a continuación en mono-parche zipfile.ZipExtFile, no es que yo recomendaría este tipo de hackers. Lo incluyo solo para ilustrar la esencia de lo que se hizo en Python 3.2 para que todo funcione bien.

$ cat test_zip_file_py3k.py 
import csv, io, sys, zipfile 

zip_file = zipfile.ZipFile(sys.argv[1]) 
items_file = zip_file.open('items.csv', 'rU') 
# items_file.readable = lambda: True 
# items_file.writable = lambda: False 
# items_file.seekable = lambda: False 
# items_file.read1 = items_file.read 
items_file = io.TextIOWrapper(items_file) 

for idx, row in enumerate(csv.DictReader(items_file)): 
    print('Processing row {0} -- row = {1}'.format(idx, row)) 

Si tuviera que soportar py3k < 3.2, entonces me gustaría ir con la solución en my other answer.

+0

io.TextIOWrapper() FTW !!! Gracias. –

5

Puede envolverlo en un io.TextIOWrapper.

items_file = io.TextIOWrapper(items_file, encoding='your-encoding', newline='') 

Debería funcionar.

1

Lennart's answer está en el camino correcto (Gracias, Lennart, he votado en su respuesta) Y es casi obras:

$ cat test_zip_file_py3k.py 
import csv, io, sys, zipfile 

zip_file = zipfile.ZipFile(sys.argv[1]) 
items_file = zip_file.open('items.csv', 'rU') 
items_file = io.TextIOWrapper(items_file, encoding='iso-8859-1', newline='') 

for idx, row in enumerate(csv.DictReader(items_file)): 
    print('Processing row {0}'.format(idx)) 

$ python3.1 test_zip_file_py3k.py ~/data.zip 
Traceback (most recent call last): 
    File "test_zip_file_py3k.py", line 7, in <module> 
    items_file = io.TextIOWrapper(items_file, 
            encoding='iso-8859-1', 
            newline='') 
AttributeError: readable 

El problema parece ser que el primer parámetro requerido io.TextWrapper 's es una memoria intermedia; no es un objeto de archivo

Esto parece funcionar:

items_file = io.TextIOWrapper(io.BytesIO(items_file.read())) 

Esto parece un poco complejo y también parece molesto tener que leer en su conjunto (tal vez enormes) archivo zip en la memoria. ¿Alguna mejor manera?

Aquí está en acción:

$ cat test_zip_file_py3k.py 
import csv, io, sys, zipfile 

zip_file = zipfile.ZipFile(sys.argv[1]) 
items_file = zip_file.open('items.csv', 'rU') 
items_file = io.TextIOWrapper(io.BytesIO(items_file.read())) 

for idx, row in enumerate(csv.DictReader(items_file)): 
    print('Processing row {0}'.format(idx)) 

$ python3.1 test_zip_file_py3k.py ~/data.zip 
Processing row 0 
Processing row 1 
Processing row 2 
... 
Processing row 250 
+0

[Otra respuesta mía] (http://stackoverflow.com/questions/5627954/py3k-how-do-you-read-a-file-inside-a-zip-file-as-text-not- bytes/5639960 # 5639960) describe una mejor manera que funciona en Python 3.2. –

Cuestiones relacionadas