Подтвердить что ты не робот

Как вы читаете файл внутри zip файла в виде текста, а не байтов?

Простая программа для чтения CSV файла внутри zip файла работает в Python 2.7, но не в 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?)

Итак, модуль csv в Python 3 хочет видеть текстовый файл, но zipfile.ZipFile.open возвращает zipfile.ZipExtFile, который всегда обрабатывается как двоичные данные.

Как сделать эту работу в Python 3?

4b9b3361

Ответ 1

Я просто заметил, что Леннарт ответ не работает с Python 3.1, но он работает с Python 3.2. Они усовершенствовали zipfile.ZipExtFile в Python 3.2 (см. Примечания к выпуску). Похоже, что эти изменения заставляют zipfile.ZipExtFile работать с io.TextWrapper.

Кстати, это работает в Python 3.1, если вы раскомментируете приведенные ниже zipfile.ZipExtFile строки в zipfile.ZipExtFile monkey- zipfile.ZipExtFile, не то чтобы я рекомендовал этот вид хакерства. Я включил его только для иллюстрации сути того, что было сделано в Python 3.2, чтобы все работало хорошо.

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

Если бы мне пришлось поддерживать py3k <3.2, я бы пошел с решением в моем другом ответе.

Ответ 2

Вы можете обернуть его в io.TextIOWrapper.

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

Должно сработать.

Ответ 3

Ответ Леннарта находится на правильном пути (спасибо, Леннарт, я проголосовал за ваш ответ) и работает почти:

$ 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

Проблема заключается в том, что io.TextWrapper первый требуемый параметр - это buffer; а не файловый объект.

Это работает:

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

Это кажется немного сложным, и также кажется, что его надо читать в целом (возможно, огромном) zip файле в память. Любой лучший способ?

Здесь он находится в действии:

$ 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

Ответ 4

И если вам просто нравится читать файл в строку:

with ZipFile('spam.zip') as myzip:
    with myzip.open('eggs.txt') as myfile:
       eggs = myfile.read().decode('UTF-8'))