您如何将 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:迭代器应返回字符串,而不是字节(您是不是以文本模式打开文件?)