【问题标题】:Why doesn't this conversion to utf8 work?为什么这种转换为 utf8 不起作用?
【发布时间】:2011-11-02 01:43:01
【问题描述】:

我有一个输出一些字符的子进程命令,例如'\xf1'。我正在尝试将其解码为 utf8,但出现错误。

s = '\xf1'
s.decode('utf-8')

以上抛出:

UnicodeDecodeError: 'utf8' codec can't decode byte 0xf1 in position 0: unexpected end of data

当我使用“latin-1”时它可以工作,但 utf8 不应该也可以工作吗?我的理解是 latin1 是 utf8 的一个子集。

我错过了什么吗?

编辑:

print s # ñ
repr(s) # returns "'\\xa9'"

【问题讨论】:

  • My understanding is that latin1 is a subset of utf8 不,它不是 ASCII 是 utf8 的子集。
  • 但是 utf8 不应该在那个代码点有一个字符吗?
  • 它是utf-8中多字节序列的第一个字节,所以它本身是无效的。
  • @trinth:许多以 utf-8 编码的 unicode 字符是两个或多个字节长。 \xf1 可能是这样一个字符的一部分,即使 \xf1 本身不解码。您能否发布更长的输出 repr 的 sn-p 以及(如果可能)它应该代表什么?
  • 我在上面添加了输出。输出是字符串类型,可以包含许多其他字符。上面的代码点只是一个例子。

标签: python unicode encoding utf-8


【解决方案1】:

您将 Unicode 与 UTF-8 混淆了。 Latin-1 是 Unicode 的子集,但不是 UTF-8 的子集。 避免像瘟疫一样考虑单个代码单元。只需使用代码点。不要考虑 UTF-8。请考虑使用 Unicode。这就是你感到困惑的地方。

演示程序源代码

在 Python 中使用 Unicode 非常容易。尤其是 Python 3 和宽版本,这是我使用 Python 的唯一方式,但如果您小心坚持使用 UTF-8,您仍然可以在窄版本下使用旧版 Python 2。

为此,请始终将您的源代码编码和输出编码正确地转换为 UTF-8。现在停止考虑 UTF-anything,在整个 Python 程序中只使用 UTF-8 文字、逻辑代码点编号或符号字符名称。

这是带有行号的源代码:

% cat -n /tmp/py
     1  #!/usr/bin/env python3.2
     2  # -*- coding: UTF-8 -*-
     3  
     4  from __future__ import unicode_literals
     5  from __future__ import print_function
     6  
     7  import sys
     8  import os
     9  import re
    10  
    11  if not (("PYTHONIOENCODING" in os.environ)
    12              and
    13          re.search("^utf-?8$", os.environ["PYTHONIOENCODING"], re.I)):
    14      sys.stderr.write(sys.argv[0] + ": Please set your PYTHONIOENCODING envariable to utf8\n")
    15      sys.exit(1)
    16  
    17  print('1a: el ni\xF1o')
    18  print('2a: el nin\u0303o')
    19  
    20  print('1a: el niño')
    21  print('2b: el niño')
    22  
    23  print('1c: el ni\N{LATIN SMALL LETTER N WITH TILDE}o')
    24  print('2c: el nin\N{COMBINING TILDE}o')

这里是带有非 ASCII 字符 uniquoted 的打印函数,使用 \x{⋯} 表示法:

% grep -n ^print /tmp/py | uniquote -x
17:print('1a: el ni\xF1o')
18:print('2a: el nin\u0303o')
20:print('1b: el ni\x{F1}o')
21:print('2b: el nin\x{303}o')
23:print('1c: el ni\N{LATIN SMALL LETTER N WITH TILDE}o')
24:print('2c: el nin\N{COMBINING TILDE}o')

演示程序的示例运行

这是该程序的示例运行,显示了执行此操作的三种不同方式(a、b 和 c):第一个设置为源代码中的文字(将受 StackOverflow 的 NFC 转换的影响,因此不能值得信赖!!!),第二组分别带有 numeric Unicode code pointssymbolic Unicode character names,同样是 uniquoted,因此您可以看到真正的内容:

% python /tmp/py
1a: el niño
2a: el niño
1b: el niño
2b: el niño
1c: el niño
2c: el niño

% python /tmp/py | uniquote -x
1a: el ni\x{F1}o
2a: el nin\x{303}o
1b: el ni\x{F1}o
2b: el nin\x{303}o
1c: el ni\x{F1}o
2c: el nin\x{303}o

% python /tmp/py | uniquote -v
1a: el ni\N{LATIN SMALL LETTER N WITH TILDE}o
2a: el nin\N{COMBINING TILDE}o
1b: el ni\N{LATIN SMALL LETTER N WITH TILDE}o
2b: el nin\N{COMBINING TILDE}o
1c: el ni\N{LATIN SMALL LETTER N WITH TILDE}o
2c: el nin\N{COMBINING TILDE}o

我真的不喜欢看二进制,但这是二进制字节的样子:

% python /tmp/py | uniquote -b
1a: el ni\xC3\xB1o
2a: el nin\xCC\x83o
1b: el ni\xC3\xB1o
2b: el nin\xCC\x83o
1c: el ni\xC3\xB1o
2c: el nin\xCC\x83o

故事的寓意

即使您使用 UTF-8 源代码,您也应该只考虑并使用逻辑 Unicode 代码点编号(或符号命名字符),而不是构成 UTF-8 串行表示的单个 8 位代码单元(或UTF-16 的问题)。需要代码单元而不是代码点是极其罕见的,它只会让您感到困惑。

如果您使用更广泛的 Python3 版本,您将获得比使用这些选择的替代方案更可靠的行为,但这是 UTF-32 问题,而不是 UTF-8 问题。如果您顺其自然,UTF-32 和 UTF-8 都很容易使用。

【讨论】:

    【解决方案2】:

    UTF-8 不是 Latin-1 的子集。 UTF-8 使用相同的单个字节对 ASCII 进行编码。对于所有其他代码点,都是多个字节。

    简单地说,\xf1 不是有效的 UTF-8,正如 Python 告诉你的那样。 “Unexpected end of input”表示该字节标志着一个未提供的多字节序列的开始。

    我建议你阅读UTF-8

    【讨论】:

      【解决方案3】:

      它是 UTF-8 中多字节序列的第一个字节,所以它本身是无效的。

      其实就是4字节序列的第一个字节。

      Bits Last code point Byte 1   Byte 2   Byte 3   Byte 4   Byte 5   Byte 6
      21   U+1FFFFF        11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
      

      请参阅here 了解更多信息。

      【讨论】:

        【解决方案4】:

        我的理解是 latin1 是 utf8 的一个子集。

        错了。 Latin-1,又名ISO 8859-1(有时错误地称为Windows-1252)不是UTF-8 的子集。另一方面,ASCII UTF-8 的一个子集。 ASCII 字符串是有效的 UTF-8 字符串,但通用的 Windows-1252 或 ISO 8859-1 字符串不是有效的 UTF-8,这就是 s.decode('UTF-8') 抛出 UnicodeDecodeError 的原因。

        【讨论】:

        • 错误: Windows-1252 ≠ Latin-1。 ISO-8859-1 == Latin-1。
        • 更具体地说,CP1252 具有可打印字符,而 ISO-8859-1 具有 C1 控制字符。
        • @tchirst:我知道区别。我希望我的编辑足以让你删除你的反对票,如果那是你的话。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-20
        • 2019-09-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多