38

用于读取 zip 文件中的 CSV 文件的简单程序适用于 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?)

所以csvPython 3 中的模块想要查看一个文本文件,但zipfile.ZipFile.open返回一个zipfile.ZipExtFile始终被视为二进制数据的文件。

如何在 Python 3 中完成这项工作?

4

5 回答 5

33

我刚刚注意到Lennart 的答案不适用于 Python 3.1,但它确实适用于Python 3.2。它们在 Python 3.2 中得到了增强zipfile.ZipExtFile(参见发行说明)。这些更改似乎可以zipfile.ZipExtFile很好地与io.TextWrapper.

顺便说一句,它可以在 Python 3.1 中使用,如果您取消注释下面的 hacky 行到 monkey-patch zipfile.ZipExtFile,我不会推荐这种hacky。我包含它只是为了说明 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,那么我会在我的另一个答案中使用该解决方案。

于 2011-04-12T18:34:35.250 回答
14

您可以将其包装在io.TextIOWrapper中。

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

应该管用。

于 2011-04-12T07:25:51.957 回答
9

如果您只是想将文件读入字符串:

with ZipFile('spam.zip') as myzip:
    with myzip.open('eggs.txt') as myfile:
       eggs = myfile.read().decode('UTF-8'))
于 2019-06-26T17:21:12.397 回答
3

Lennart 的答案是正确的(谢谢,Lennart,我投票赞成你的答案),它几乎可以工作:

$ 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的第一个必需参数是缓冲区;不是文件对象。

这似乎有效:

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
于 2011-04-12T17:59:08.980 回答
0

从 Python 3.8 开始,可以使用更漂亮的使用Path对象的方法:

zipfile = zipfile.Path(sys.argv[1], at='items.csv')
items_file = zipfile.read_text()

for idx, row in enumerate(csv.DictReader(items_file)):
    print('Processing row {0}'.format(idx))
于 2022-01-04T18:29:15.867 回答