有没有简单的方法让Python支持Unicode?

12
我正在尝试处理Python 2.7.2中的Unicode问题。我知道有一个`.encode('utf-8')`的方法,但一半的时间添加它会导致错误,而另一半的时间不添加它也会导致错误。
有没有办法告诉Python - 我认为这是最新和现代的语言,只使用Unicode字符串,而不让我使用`.encode('utf-8')`之类的东西?
我知道... Python 3.0应该可以做到这一点,但我不能使用3.0,而且2.7并不算太老...
例如:
url = "http://en.wikipedia.org//w/api.php?action=query&list=search&format=json&srlimit=" + str(items) + "&srsearch=" + urllib2.quote(title.encode('utf-8'))
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 19: ordinal not in range(128)

更新 如果我从所有代码中删除所有的.encode语句,并在#!/usr/bin/python的下面添加# -*- coding: utf-8 -*-,那么我得到的结果与如果我根本没有添加# -*- coding: utf-8 -*-一样。

/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py:1250: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
  return ''.join(map(quoter, s))
Traceback (most recent call last):
  File "classes.py", line 583, in <module>
    wiki.getPage(title)
  File "classes.py", line 146, in getPage
    url = "http://en.wikipedia.org/w/api.php?action=query&prop=revisions&format=json&rvprop=content&rvlimit=1&titles=" + urllib2.quote(title)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 1250, in quote
    return ''.join(map(quoter, s))
KeyError: u'\xf1'

我并不手动输入任何字符串,而是从网站解析HTML和JSON。因此,这些脚本/字节流/无论它们是什么,都是由Python创建的。
更新2:我可以将错误移动到其他地方,但它只是在新的位置上不断出现。我原本希望Python能成为一个有用的脚本工具,但看起来三天没有运气,我将尝试使用另一种语言。很遗憾,Python已预安装在OSX上。我已标记正确的答案,以解决我发布的一个错误实例。

请注意,我刚刚发布了一个相关问题,深入探讨了这个问题的一个方面:https://dev59.com/YGjWa4cB1Zd3GeqPt8eQ - Mu Mind
6
请立即阅读http://www.joelonsoftware.com/articles/Unicode.html。如果一个人不理解至少这篇文章中的内容,就算是使用_text_都无法制作一个正常工作的程序,更别说正确处理编码转换了。从你的问题措辞中可以明显看出你在盲目尝试,建议先学习一下这个。 - jsbueno
2
@jsbueno - 我知道什么是 Unicode,我知道它的工作原理。但 Python 把它搞砸了,以至于你必须盲目尝试才能使用它。 - Justin808
不,你不需要这样做。如果你_理解_它的工作原理,Python使用它的方式非常明智,就像上面的链接中很好地解释的那样。 - jsbueno
顺便说一下,这不是冒犯之语,请阅读文章,您会更有信心不仅完成手头的任务,而且处理Python文本问题也不在话下。 - jsbueno
5个回答

20

这是一个很久以前的问题,但我想补充一部分建议。虽然我理解楼主的痛苦——因为我自己也经历了很多次——但是以下是一个(部分)答案,可以让事情 “更容易”。将以下代码放在任何 Python 2.7 脚本的顶部:

from __future__ import unicode_literals

这至少会确保您自己的字面字符串默认为 unicode 而不是 str。


这似乎是对OP(主要)问题最直接的答案。 - ᴠɪɴᴄᴇɴᴛ

18

除了在所有地方都使用unicode字符串并立即解码任何接收到的编码字符串之外,没有办法使Unicode“只是运作良好”。问题在于您必须始终清楚您正在处理编码或未编码的数据,或者使用可以为您跟踪它的工具,否则您将度过糟糕的时光。

Python 2对此进行了一些有问题的操作:例如,它将str设置为像字符串字面量这样的默认值,而不是unicode,当您将两者相加时,它会默默地将str强制转换为unicode。也让您在已编码的字符串上调用.encode()以进行双重编码。因此,存在许多python程序员和python库,它们不知道它们设计要使用哪种编码方式,但仍然设计为处理某些特定的编码方式,因为str类型旨在让程序员自己管理编码。每次使用这些库时都必须考虑编码方式,因为它们不支持unicode类型本身。


在您的特殊情况下,第一个错误告诉您正在处理UTF-8编码数据,并尝试对其进行双重编码,而第二个错误告诉您正在处理未编码的数据。看起来您可能两种情况都有。您应该找到并修复问题的来源(我怀疑它与上面提到的默默强制转换有关),但是这里有一个应急方法可以解决问题:

encoded_title = title
if isinstance(encoded_title, unicode):
    encoded_title = title.encode('utf-8')
如果这确实是无声的强制困扰着你,你可以使用优秀的 unicode-nazi 工具轻松追踪问题:
python -Werror -municodenazi myprog.py

这将会在unicode泄漏到非unicode字符串的那一点给你一个回溯,而不是试图在离实际问题很远的地方解决这个异常。有关详细信息,请参见我在相关问题上的答案。


1
嗯,这个方法确实可行,但只是把错误移到了另一个地方。看来我得用另一种语言重新编写所有内容了。我本来希望Python能成为一个有用的脚本工具,但三天后,它并没有。 - Justin808
如果这样可以解决您的错误,那太好了!这证实了您的问题是Unicode字符串与非Unicode字符串混合在一起。那些不良数据仍然存在于某个地方,而另一个错误很可能只是同一原始问题的另一个症状。我刚刚更新了答案,提到了Unicode-nazi工具,它应该让您轻松找出根本问题。 - Mu Mind
看起来 HTMLParser 不支持 Unicode?我就是不明白为什么处理 Unicode 要这么难。它应该对开发者完全隐藏,这是底层的东西。甚至 obj-c 都将其隐藏起来,一切都能正常工作。 - Justin808
在大多数情况下,它应该对开发人员隐藏起来,但有些人认为这些东西的速度比正确性更重要,而更改核心库的过程非常缓慢,以至于在 Python 3 对此做出任何处理之前,这个问题一直存在。 - Mu Mind
我认为一切都在运作中……至少没有错误。我想我的一些 Unicode 转换成了 ASCII(带重音符号的 a 变成了普通的 a),但我不认为这是可能的,所以我必须查看是否有一些坏数据从我的某个来源中滑入。我最后遇到的错误是由于“print”引起的。我最终放弃了,直接打开一个文件进行写入。 - Justin808
显示剩余3条评论

3

是的,将你的Unicode数据定义为Unicode字面量:

>>> u'Hi, this is unicode: üæ'
u'Hi, this is unicode: üæ'

通常您需要使用'\uxxxx` unicode转义或设置源代码编码。例如,以下代码可以在模块顶部设置编码为UTF-8:

# -*- coding: utf-8 -*-

请阅读Python Unicode HOWTO,了解默认编码等详细信息(例如,默认源代码编码为ASCII)。
至于您的特定示例,您的标题不是Unicode文字,而是python字节字符串,python正在尝试将其解码为Unicode,以便您可以再次进行编码。这会失败,因为此类自动编码的默认编解码器是ASCII:
>>> 'å'.encode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

编码仅适用于实际的Unicode字符串,因此字节字符串需要被显式地解码:

>>> 'å'.decode('utf-8').encode('utf-8')
'\xc3\xa5'

如果您习惯使用Python 3,那么在Python 2中,unicode字面值(u'')是Python 3中的默认字符串类型,而Python 2中的常规(字节)字符串('')与Python 3中的bytes对象相同(b'')。
如果在title上调用编码和不调用编码都出现错误,则表示数据混合。请测试标题并根据需要进行编码。
if isinstance(title, unicode):
    title = title.encode('utf-8')

您可能需要查找产生混合Unicode /字节字符串标题的原因,并更正该源以始终生成其中一个。

嗨,我尝试了 # -*- coding: utf-8 -*- 但似乎它没有起作用。 - Justin808
@Justin808:阅读链接中的HOWTO。哦,还有这篇Joel Spolsky文章。你的title是一个字节字符串,不需要再次进行encode - Martijn Pieters
这是一个循环吗?还有多个 title 吗?我怀疑有些是 Unicode,有些不是。 - Martijn Pieters
2
尝试使用 if isinstance(title, unicode): title=title.encode('utf-8');你几乎肯定有混合数据。 - Martijn Pieters

2

实际上,让Python支持unicode最简单的方法是使用Python 3,因为默认情况下所有内容都是unicode。

不幸的是,针对P3编写的库不多,而且在编码和关键字使用方面存在基本差异。这就是我的问题所在:我需要的库仅适用于P2.7,而我不知道如何将它们转换为P3。 :(


这是我找到的解决此问题的最佳答案。 - james-see

2

请确保您的title.encode("utf-8")中的title是unicode类型,不要使用str("İŞşĞğÖöÜü")。

在您的字符串转换器中,请使用unicode("ĞğıIİiÖöŞşcçÇ")。


1
如果没有明确的编码,你是无法这样做的。unicode('å') 会出现相同的 UnicodeDecodeError 错误。 - Martijn Pieters

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