Как вы читаете файл внутри zip-файла в виде текста, а не байтов? - PullRequest
29 голосов
/ 12 апреля 2011

Простая программа для чтения 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?

Ответы [ 4 ]

30 голосов
/ 12 апреля 2011

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

Кстати, это работает в Python 3.1, если вы раскомментируете приведенные ниже хакерские строки в monkey-patch 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, я бы пошел с решением в <a href="/5293978/kak-vy-chitaete-fail-vnutri-zip-faila-v-vide-teksta-a-ne-baitov#5293993">мой другой ответ .

7 голосов
/ 12 апреля 2011

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

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

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

2 голосов
/ 12 апреля 2011

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

$ 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
0 голосов
/ 26 июня 2019

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

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