您如何将 zip 文件中的文件作为文本而不是字节读取?

Posted

技术标签:

【中文标题】您如何将 zip 文件中的文件作为文本而不是字节读取?【英文标题】:How do you read a file inside a zip file as text, not bytes? 【发布时间】:2011-08-03 10:22:53 【问题描述】:

用于读取 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?)

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

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

【问题讨论】:

【参考方案1】:

我刚刚注意到 Lennart's answer 不适用于 Python 3.1,但它确实适用于 Python 3.2。他们在 Python 3.2 中增强了zipfile.ZipExtFile(参见release notes)。这些更改似乎使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 my other answer 中的解决方案。

【讨论】:

请注意,在 3.6 中删除了对“U”模式的支持:docs.python.org/3/library/zipfile.html#zipfile.ZipFile.open【参考方案2】:

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

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

应该可以。

【讨论】:

【参考方案3】:

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

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

【讨论】:

【参考方案4】:

Lennart's answer 在正确的轨道上(谢谢,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

【讨论】:

Another answer from me 描述了一种在 Python 3.2 中工作的更好方法。【参考方案5】:

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

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

【讨论】:

以上是关于您如何将 zip 文件中的文件作为文本而不是字节读取?的主要内容,如果未能解决你的问题,请参考以下文章

将 .csv 文件从 URL 读取到 Python 3.x - _csv.Error:迭代器应返回字符串,而不是字节(您是不是以文本模式打开文件?)

在 Powershell 中读取 ZIP 中的文本文件

Python3 - 读写字节数据

如何获取最新的 JRE / JDK 作为 zip 文件而不是 EXE 或 MSI 安装程序? [关闭]

Android查看zip文件而不解压

命令行参数作为字节而不是python3中的字符串