为什么这个转换为utf8不起作用?

时间:2011-08-23 15:24:24

标签: python unicode encoding utf-8

我有一个子进程命令输出一些字符,例如'\ 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'"

4 个答案:

答案 0 :(得分:8)

您将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')

以下是使用\x{⋯}表示法的非ASCII字符uniquoted的打印函数:

% 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转换的影响,因此不能信任!!!)和后两组分别用数字Unicode代码点符号Unicode字符名称再次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都很容易使用,如果你只是顺其自然。

答案 1 :(得分:4)

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

简而言之,正如Python告诉你的那样,\ xf1无效UTF-8。 “意外的输入结束”表示该字节标志着未提供的多字节序列的开始。

我建议你阅读UTF-8

答案 2 :(得分:1)

  

我的理解是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的原因。

答案 3 :(得分:1)

它是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