Как вы читаете файл внутри 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?
Ответы
Ответ 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'))