【问题标题】:How to open an unicode text file inside a zip?如何在 zip 中打开 unicode 文本文件?
【发布时间】:2014-01-03 07:19:27
【问题描述】:

我试过了

with zipfile.ZipFile("5.csv.zip", "r") as zfile:
    for name in zfile.namelist():
        with zfile.open(name, 'rU') as readFile:
                line = readFile.readline()
                print(line)
                split = line.split('\t')

它回答:

b'$0.0\t1822\t1\t1\t1\n'
Traceback (most recent call last)
File "zip.py", line 6
    split = line.split('\t')
TypeError: Type str doesn't support the buffer API

如何以 unicode 而不是 b 打开文本文件?

【问题讨论】:

  • 在我看来,zip 库不支持open 的编码参数。如果这是正确的,我认为您将不得不使用 codecs.EncodedFile 包装器或手动解码每一行。
  • 您知道文件的正确编码吗?对我来说它看起来像 utf-8,但猜测是否可以避免它是个坏主意。

标签: python file-io python-3.x unicode zip


【解决方案1】:

要将字节流转换为 Unicode 流,您可以使用io.TextIOWrapper()

encoding = 'utf-8'
with zipfile.ZipFile("5.csv.zip") as zfile:
    for name in zfile.namelist():
        with zfile.open(name) as readfile:
            for line in io.TextIOWrapper(readfile, encoding):
                print(repr(line))

注意:TextIOWrapper() 默认使用通用换行模式。 zfile.open() 中的 rU 模式自 3.4 版起已弃用。

它避免了@Peter DeGlopper's answer 中描述的多字节编码问题。

【讨论】:

  • 是的,如果这适用于类文件对象,这可能是在打开文件时无法提供编码(或使用codecs.open 方法)时最安全的答案。我已经看到了建议使用它来代替rU 模式,但由于3.4 尚未发布,弃用警告似乎并不适用。即使没有这个,正确处理多字节换行也是一个令人信服的理由——我想知道这与弃用的决定有多大关系?
  • 它确实有效。这是在 Python 3 中执行此操作的方式。(subprocess 模块中的 sys.stdin、sys.stdout、(文本)管道是使用 TextIOWrapper 创建的)。 codecs.open 需要一个文件名(我看不出它对 zipfile 有什么帮助)。 codecs.getreader(encoding)(file_obj) 可能对 Python 2 有用。
  • 是的,我在考虑一般情况 - 我没有看到 zipfile 的更好选择。也许我的评论会更好的措辞“鉴于它适用于类似文件的对象......”
【解决方案2】:

edit 对于 Python 3,使用 io.TextIOWrapper 作为 this answer 描述是最佳选择。下面的答案对 2.x 仍然有帮助。即使对于 3.x,我也不认为下面的任何内容实际上是不正确的,但 io.TestIOWrapper 仍然更好。

如果文件是 utf-8,这将起作用:

# the rest of the code as above, then:
with zfile.open(name, 'rU') as readFile:
    line = readFile.readline().decode('utf8')
    # etc

如果您要对文件进行迭代,您可以使用codecs.iterdecode,但这不适用于readline()

with zfile.open(name, 'rU') as readFile:
    for line in codecs.iterdecode(readFile, 'utf8'):
        print line
        # etc

请注意,对于多字节编码,这两种方法都不一定安全。例如,little-endian UTF-16 表示带有字节b'\x0A\x00' 的换行符。寻找换行符的非 unicode 感知工具将错误地拆分,将空字节留在下一行。在这种情况下,您必须使用不会尝试用换行符分割输入的东西,例如ZipFile.read,然后一次解码整个字节字符串。这不是 utf-8 的问题。

【讨论】:

  • 我已经检查过了,确实 .readline().decode('utf-16') 失败并出现异常。更糟糕的是,codecs.iterdecode() 默默地产生错误的输出(换行符移到下一行)。 io.TextIOWrapper() 避免了此类问题。
  • 有人告诉我 Python 3 一劳永逸地解决了 Unicode 错误。我错过了什么?
  • @oneloop - 好吧,我不知道你错过了什么,你没有给出任何上下文。 Python 3 使意外混合编码字节和 Unicode 对象变得更加困难,这是 Python 2 中混淆错误的常见原因,因为它显然可以工作,直到您使用默认字符编码实际导致隐式解码/编码的数据。但这并不神奇,您仍然需要解码以将二进制数据转换为字符或进行编码以进行相反的操作。
【解决方案3】:

您看到该错误的原因是您试图将字节与 unicode 混合。 split 的参数也必须是字节串:

>>> line = b'$0.0\t1822\t1\t1\t1\n'
>>> line.split(b'\t')
[b'$0.0', b'1822', b'1', b'1', b'1\n']

要获取 unicode 字符串,请使用 decode:

>>> line.decode('utf-8')
'$0.0\t1822\t1\t1\t1\n'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    • 2014-11-07
    • 2021-05-02
    • 2023-04-03
    • 2021-08-11
    • 1970-01-01
    • 2011-01-28
    相关资源
    最近更新 更多