使用Python处理UTF-8编码问题

3
作为现在夏天,我决定学习一门新的语言,并选择了Python。实际上,我想学习的是如何使用Python操纵阿拉伯文本。现在,我已经找到了许多关于使用Python的资源,这些资源非常棒。然而,当我将所学应用于阿拉伯字符串时,我得到的是数字和字母混合在一起。
以英文为例:
>>> ebook = 'The American English Dictionary'
>>> ebook[2]
'e'

现在,是阿拉伯语:

>>> abook = 'القاموس العربي'
>>> abook[2]
'\xde'                  #the correct output should be 'ق'

然而,使用print是没有问题的,就像这样:

>>> print abook[2]
ق

我需要修改什么才能使Python始终识别阿拉伯字母?

8
请使用Python 3或Unicode字面量:u"القاموس العربي" - Oleh Prypin
我非常推荐您观看2012年Pycon大会上的这个演讲实用Unicode,或者,如何停止痛苦?。观看后,您应该会更加熟悉Unicode,并且如果您正在处理阿拉伯语,那么这将是必不可少的。 - root
嗨,感谢您的回复。我应用了Unicode字面值,得到了以下结果:
tmp = u"القاموس العربي" tmp u'\xc7\xe1\xde\xc7\xe3\xe6\xd3 \xc7\xe1\xda\xd1\xc8\xed' print tmp ÇáÞÇãæÓ ÇáÚÑÈí
- Favn Hghksd
1
通常情况下,你的I/O设备不支持Unicode -- 例如Windows上的cmd.exe。在这种情况下,Python表现得很好,只是你看不到它。 - Katriel
如果是这样的话,那么:>>> print abook[2] 就不会正确打印出来。但实际上它确实可以。这意味着如果我在变量前使用 'print',它将返回正确的 Unicode 字母。如果我省略 'print',它将打印出 '\xde'。 - Favn Hghksd
@FavnHghksd:ق'\xd9\x82'在输出中的区别是print 'ق'print repr('ق')之间的区别。默认情况下(sys.displayhook),REPL会显示对象的repr(一个无歧义的表示,旨在满足o == eval(repr(o)))。print 'ق'会产生一个更易读的(但可能有歧义的)表示。 - jfs
4个回答

4

明确使用Unicode:

>>> s = u'القاموس العربي'
>>> s
u'\u0627\u0644\u0642\u0627\u0645\u0648\u0633 \u0627\u0644\u0639\u0631\u0628\u064a'
>>> print s
القاموس العربي

>>> print s[2]
ق

甚至可以逐个字符地进行操作:
>>> for i, c in enumerate(s):
...     print i,c
... 
0 ا
1 ل
2 ق
3 ا
4 م
5 و
6 س
7  
8 ا
9 ل
10 ع
11 ر
12 ب
13 ي
14 

我推荐阅读Python Unicode页面,该页面简洁实用且富有实践价值。点击此处访问。

谢谢您的回复。我尝试了您的第一组代码,但是我没有得到与您相同的结果:>>> s = u'القاموس العربي'
s u'\xc7\xe1\xde\xc7\xe3\xe6\xd3 \xc7\xe1\xda\xd1\xc8\xed' print s ÇáÞÇãæÓ ÇáÚÑÈí print s[2] Þ
- Favn Hghksd
1
这很可能是与 shell 的编码有关的问题。 - dav1d
我正在使用Windows XP Professional和Python GUI IDLE shell。 - Favn Hghksd

3
请使用Python 3.x:现在字符串是Unicode格式的-请参见Python 3的新变化
>>> abook = 'القاموس العربي'
>>> abook[0]
'ا'
>>> abook[4]
'م'

5
“使用Python 3”不是一个答案。 - Felix
我本来想使用Python 3,但很多人都说它不是完全兼容的(甚至我一直在用来学习Python的许多网站都建议避免下载最新版本)。 - Favn Hghksd
@FavnHghksd,你会发现现在大多数主要的第三方库都是兼容的 - 除非你有一个特别坚持使用的库,否则Py3比Py2更适合你。 - lvc

1
如果您需要输入:
>>> abook[2]

产生以下输出:
'ق'

这是不可能的。交互式 shell 打印 repr(abook[2]),它总是使用转义序列来表示阿拉伯字符。我不知道确切的规则,但我猜测 ASCII 宇宙之外的大多数字符都会被转义。要使其按照广告所述工作,您可以使用 u 前缀,但它仍将输出一个转义序列(尽管这次是正确的):

>>> abook = u'القاموس العربي'
>>> abook[2]
u'\u0642'

你得到 '\xde' 的原因是没有使用 u 前缀,abook 保存了该短语的 UTF-8 编码。我的输出与你的不同(可能是因为通过复制粘贴改变了代码点;我不确定),但原则仍然适用:
>>> abook = 'القاموس العربي'
>>> ' '.join( hex(ord(c))[-2:] for c in abook )
'd8 a7 d9 84 d9 82 d8 a7 d9 85 d9 88 d8 b3 20 d8 a7 d9 84 d8 b9 d8 b1 d8 a8 d9 8a'
>>> abook[2]
'\xd9'

您可以按照以下步骤进行确认:
>>> abook = 'القاموس العربي'
>>> unicode(abook, 'utf-8')[2]
u'\u0642'
>>> print unicode(abook, 'utf-8')[2]
ق

嗨,Marcelo,谢谢你的回复。我尝试了你的第一条语句,但是我得到的结果与你不同。这是我得到的:>>> abook = u'القاموس العربي'
abook[2] u'\xde'
- Favn Hghksd
@FavnHghksd:我在我的回答中已经解决了这个问题。“我的输出与你的不同……”我恐怕无法提供更多的见解了。 - Marcelo Cantos
我遇到了这个异常:“TypeError:不支持解码Unicode”,请帮忙。 - William Kinaan
@WilliamKinaan:请提出一个单独的问题,并发布您场景的详细信息。(如果您认为这会提供有用的上下文,可以链接回此答案。) - Marcelo Cantos

0

根据问题评论中的结果,这似乎是由于repr引起了mojibake问题 - 也就是说,它对编码感到困惑并使用了错误的编码。 print将尝试使用它认为你的STDOUT使用的编码,并直接打印生成的字节 - repr尝试打印一个ASCII安全的表示,但在这种情况下似乎失败了。

好消息是 - 这是repr的问题,而不是Python的Unicode处理问题。只要往返旅行:s.encode('utf8').decode('utf8') == s正常工作,你就没问题了。当您想要检查值时,请print该值,不要仅在交互式终端中提及它,并且在任何地方都使用Unicode字符串(使用Py3将大大有助于此,或者至少执行以下操作:

from __future__ import unicode_literals
from io import open

保持编码跟踪,即使repr做了一些奇怪的事情,您的程序也会正常工作。

还要注意,您的问题与UTF8无关 - 它涉及Unicode,这是一个不同(但相关)的概念。如果您阅读的资源没有强调这种差异,请获取更好的资源 - 对这些概念的误解将导致许多痛苦。


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