如何在Python中将Unicode转换为字符串?

15
以下 Unicode 和字符串在明确定义时可以独立存在:
>>> value_str='Andr\xc3\xa9'
>>> value_uni=u'Andr\xc3\xa9'
еҰӮжһңжҲ‘еҸӘжңүеғҸдёҠйқўдёҖж ·еҲҶй…Қз»ҷеҸҳйҮҸзҡ„`u'Andr\xc3\xa9'`пјҢеҰӮдҪ•еңЁPython 2.5жҲ–2.6дёӯе°Ҷе…¶иҪ¬жҚўдёә`'Andr\xc3\xa9'`пјҹ зј–иҫ‘пјҡ жҲ‘еҒҡдәҶд»ҘдёӢдәӢжғ…пјҡ
>>> value_uni.encode('latin-1')
'Andr\xc3\xa9'

这解决了我的问题。有人能向我解释一下到底发生了什么吗?


这是你在不到一天内提出的第三个问题,都基于同样的误解。u'Andr\xc3\xa9'是通过utf8和latin1的双重编码得到的无意义字符串。千万不要这样做! - John Machin
这就是让我感到困惑的地方。它是如何从最初的重音变成现在的样子的?当你说用utf8和latin1进行双重编码时,这是总共3种编码吗(2个utf8 + 1个latin1)?从最初状态到当前状态的编码顺序是什么? - Thierry Lam
7个回答

16

您似乎搞混了编码方式。您真正想要的可能是 u'Andr\xe9',它等同于'André'

但是,您似乎有一个被错误解码的UTF-8编码。您可以通过将Unicode字符串转换为普通字符串来修复它。我不确定最好的方法是什么,但这似乎有效:

>>> ''.join(chr(ord(c)) for c in u'Andr\xc3\xa9')
'Andr\xc3\xa9'

然后正确地进行解码:

>>> ''.join(chr(ord(c)) for c in u'Andr\xc3\xa9').decode('utf8')
u'Andr\xe9'    

现在它已经处于正确的格式中。

然而,如果可能的话,你应该尝试弄清楚为什么数据一开始被错误地编码,并在那里解决这个问题,而不是进行修复。


6

如果你有u'Andr\xc3\xa9',那么这是一个从错误编码的字节字符串解码出来的Unicode字符串。正确的编码是UTF-8。为了将其转换回字节字符串以便正确解码,你可以使用你发现的技巧。Unicode的前256个代码点与ISO-8859-1(别名latin1)编码是1:1映射关系。所以:

>>> u'Andr\xc3\xa9'.encode('latin1')
'Andr\xc3\xa9'

现在它是一个字节字符串,可以使用utf8正确解码:
>>> 'Andr\xc3\xa9'.decode('utf8')
u'Andr\xe9'
>>> print 'Andr\xc3\xa9'.decode('utf8')
André

一步完成:
>>> print u'Andr\xc3\xa9'.encode('latin1').decode('utf8')
André

5
您在评论中问道:“这让我感到疑惑。它是如何从原来的带口音变成现在这个样子的呢?当您说使用utf8和latin1进行双重编码时,这是3种编码(2种utf8 + 1种latin1)吗?从最初状态到当前状态的编码顺序是什么?”在Mark Byers的答案中,他说:“你似乎有一个被错误解码的UTF-8编码”。您接受了他的答案,但仍然感到困惑?好的,下面是详细描述:
注意:所有字符串都将使用(隐式)repr()显示。unicodedata.name()将用于验证内容。这样,控制台编码的变化就不会混淆字符串的解释。
初始状态:您有一个名为u1的Unicode对象。它包含了e-acute:
>>> u1 = u'\xe9'
>>> import unicodedata as ucd
>>> ucd.name(u1)
'LATIN SMALL LETTER E WITH ACUTE'

你需要将u1编码为UTF-8,并将结果命名为s:
>>> s = u1.encode('utf8')
>>> s
'\xc3\xa9'

您使用了错误的编码方式——latin1,应该使用utf8来解码s。否则得到的结果是无意义的垃圾。

>>> u2 = s.decode('latin1')
>>> u2
u'\xc3\xa9'
>>> ucd.name(u2[0]); ucd.name(u2[1])
'LATIN CAPITAL LETTER A WITH TILDE'
'COPYRIGHT SIGN'
>>>

请注意:当x!= y时,unicode_object.encode('x').decode('y)通常是无意义的;如果你幸运的话,它会引发异常;如果你不幸,它会悄悄地创建无意义的东西。同时,请理解,悄悄地创建无意义的东西并不是一个bug——Python(或任何其他语言)没有一般方法可以检测到已经犯了一个错误。这在涉及latin1时尤其适用,因为所有256个代码点都映射到前256个Unicode代码点中的1:1,所以从str_object.decode('latin1')中获取UnicodeDecodeError是不可能的。
当然,异常情况下(希望这是异常情况),您可能需要通过执行gibberish_unicode_object.encode('y').decode('x')来撤销这样的无意义操作,就像在回答您的问题时建议的那样。

4

1
只是补充一下。上面的内容可能看起来相同,但 Unicode 字面值是由对应符号的代码点组成的,而普通字符串在不知道编码的情况下是没有意义的。 - dhill
我得到的是“Andr\xc3\x83\xc2\xa9”,这和“Andr\xc3\xa9”不同,是吗? - Thierry Lam
是的,这是可以预测的。我认为没有编码能够将Unicode代码点范围在(128,256)之间的转换成相应的字节。如果我说错了请纠正我。 - dhill
转换为 utf-8 将把 \xc3 分成两个字节!而转换为 ascii 不起作用,因为 \xc3 不在 ascii 范围内。 - I. J. Kennedy
@John Machin:没错,但我指的是Unicode编码,这里我没有加形容词。我的推理是,必须至少有一个特殊字符才能构建比一个字节更大的代码点。 - dhill
显示剩余3条评论

1

OP没有转换为ASCII或UTF-8。这就是为什么建议使用encode方法不起作用的原因。尝试这个:

v = u'Andr\xc3\xa9'
s = ''.join(map(lambda x: chr(ord(x)),v))

chr(ord(x)) 这个操作可以获取 Unicode 字符的数值(适合在应用程序中使用一个字节),而 ''.join 则是将整数列表转换回普通字符串的惯用语。毫无疑问,还有更加优雅的方法。


0

简单解释。str类型只能保存0-255范围内的字符。如果您想要存储Unicode(其中包含来自更广泛范围的字符)在str中,您首先必须将Unicode编码为适合str的格式,例如UTF-8。

要执行此操作,请在str对象上调用encode方法,并将所需编码作为参数传递,例如this_is_str = value_uni.encode('utf-8')

您可以在此处阅读更长且更深入的(以及语言无关的)有关Unicode处理的文章:The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

另一篇出色的文章(这次是针对Python特定的):Unicode HOWTO


-1

看起来好像是

str(value_uni)

应该可以运行...至少在我尝试时是这样的。

编辑:事实证明,这只能工作是因为我的系统默认编码是ISO-8859-1(Latin-1),所以如果要实现跨平台版本,请尝试:

value_uni.encode('latin1')

我尝试了一下,但是出现了UnicodeEncodeError: 'ascii' codec can't encode characters in position 4-5: ordinal not in range(128)的错误。你使用的是哪个版本的Python以及在哪个操作系统上呢? - Thierry Lam
Python 2.6.4 在 Linux 上运行...虽然现在我想起来了,我的系统默认编码可能与你的不同。但我并不完全确定我的默认编码是什么。 - David Z
好的,明白了,请尝试使用新方法。 - David Z
你如何检查系统的默认编码? - Thierry Lam
@Thierry Lam,import sys; sys.getdefaultencoding() - tgray
不想催促,但既然我已经编辑了答案并包含了正确的解决方案,删除负评会很好。 - David Z

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接