py3k: Comment lire un fichier dans un fichier zip sous forme de texte et non d'octets?

un programme simple pour lire un fichier CSV à l'intérieur d'un fichier zip fonctionne en python 2.7, mais pas en Python 3.2

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

donc le module csv en Python 3 veut voir un fichier texte, mais zipfile.ZipFile.open renvoie un zipfile.ZipExtFile qui est toujours traité comme des données binaires.

Comment faire pour que cela fonctionne en Python 3?

23
demandé sur Marc Abramowitz 2011-04-12 01:46:47

3 réponses

je viens de remarquer que réponse de Lennart n'a pas fonctionné avec Python 3.1 , mais il ne travailler avec Python 3.2 . Ils ont amélioré zipfile.ZipExtFile en Python 3.2 (voir release notes ). Ces changements semblent faire en sorte que zipfile.ZipExtFile fonctionne bien avec io.TextWrapper .

Soit dit en passant, cela fonctionne en python 3.1, si vous décommentez les lignes hacky ci-dessous à Monkey-patch zipfile.ZipExtFile , non pas que je recommande ce genre de hackery. Je l'inclus seulement pour illustrer l'essence de ce qui a été fait en Python 3.2 Pour faire les choses fonctionnent 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 je devais soutenir py3k < 3.2, alors j'irais avec la solution dans mon autre réponse .

27
répondu Marc Abramowitz 2017-05-23 12:02:50

Vous pouvez l'envelopper dans un io.TextIOWrapper .

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

devrait marcher.

5
répondu Lennart Regebro 2011-04-12 17:53:41

la réponse de Lennart est sur la bonne voie (merci, Lennart, j'ai voté votre réponse) et il presque travaux:

$ 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

Le problème semble être que io.Le premier paramètre requis de TextWrapper est un buffer ; pas un objet de fichier.

Cela semble fonctionner:

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

Cela semble un peu complexe, et aussi il cela semble ennuyeux d'avoir à lire dans un fichier zip entier (peut-être énorme) dans la mémoire. Une meilleure façon?

Ici, on est dans l'action:

$ 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
1
répondu Marc Abramowitz 2017-05-23 10:30:35