Python在Mac OS X终端中的Unicode问题

8

有人能解释一下这个奇怪的事情吗:

当我在Python shell中输入以下的Cyrillic字符串时:

>>> print 'абвгд'
абвгд

但是当我输入时:
>>> print u'абвгд'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-9: ordinal not in range(128)

既然第一个字符串输出正确,我认为我的OS X终端可以表示Unicode,但是在第二种情况下却不能。为什么?

6个回答

18
>>> print 'абвгд'
абвгд

当你输入一些字符时,你的终端决定如何将这些字符表示给应用程序。你的终端可能会将这些字符编码为utf-8、ISO-8859-5甚至是只有你的终端才能理解的东西,并将它们传递给应用程序。Python将这些字符作为一些字节序列获取。然后,Python将这些字节原样打印出来,你的终端以某种方式解释它们以显示字符。由于你的终端通常以与编码相同的方式解释这些字节,所以所有内容都像你输入的那样显示。

>>> u'абвгд'

你在这里输入一些字符,这些字符以字节的形式到达Python解释器,也许是由终端进行了某种编码。使用u前缀,Python尝试将这些数据转换为Unicode格式。为了正确地执行此操作,Python必须知道您的终端使用的编码方式。在您的情况下,Python猜测您的终端编码方式是ASCII,但接收到的数据与其不匹配,因此会出现编码错误。

在交互式会话中创建Unicode字符串的直接方法如下所示:

>>> us = 'абвгд'.decode('my-terminal-encoding')

在文件中,你也可以通过特殊的模式行指定文件的编码:

# -*- encoding: ISO-8859-5 -*-
us = u'абвгд'

设置默认输入编码的其他方法,您可以查看sys.setdefaultencoding(...)sys.stdin.encoding


15

从 Python 2.6 开始,你可以使用环境变量 PYTHONIOENCODING 来告诉 Python 你的终端支持 UTF-8。最简单的方法是将以下行添加到您的 ~/.bash_profile 文件中以使其永久生效:

export PYTHONIOENCODING=utf-8

展示Python输出的Unicode字符的Terminal.app


不错的例子,特别是考虑到OSX Python构建只有微薄的sys.maxunicode == 0xffff - Dima Tisnek
我在使用sympy漂亮打印时遇到了问题,而您的技巧解决了这个问题。谢谢。 - Pouya
python -c 'print(u"\U0001F46F")': - xApple

9
除了确保您的OS X终端设置为UTF-8之外,您可能希望将python sys默认编码设置为UTF-8或更好。在/Library/Python/2.5/site-packages中创建一个名为sitecustomize.py的文件。在此文件中添加以下内容:
import sys
sys.setdefaultencoding('utf-8')

setdefaultencoding 方法仅在 site 模块中可用,并且一旦启动完成,将从 sys 命名空间中删除。因此,您需要启动一个新的 Python 解释器才能使更改生效。您可以通过使用 sys.getdefaultencoding() 随时验证当前默认编码。

如果字符尚未是 Unicode 并且您需要进行转换,请使用 decode 方法对字符串进行解码,以便将文本从其他字符集解码为 Unicode……最好指定字符集:

s = 'абвгд'.decode('some_cyrillic_charset') # makes the string unicode
print s.encode('utf-8') # transform the unicode into utf-8, then print it

这解决了我的问题,尽管repr()的解释不正确。我在我的问题中犯了一个错误(抱歉),现在已经修复了 - 我实际上是在打印u'абвгд'字符串,所以这不是一个repr()错误。事实上 - 如果我省略print语句,我就不会得到错误 - 我只会得到u'\xd0\xb0\xd0\xb1\xd0\xb2\xd0\xb3\xd0\xb4'我的猜测是默认编码 - mac-roman 能够表示西里尔字符(另一方面并没有意义...),但不能表示Unicode中的西里尔字符。我真的不明白这个 :) - disc0dancer
不要更改系统默认编码;而是修复您的Unicode值。更改默认编码可能会破坏依赖于默认行为的库。在执行此操作之前,必须强制重新加载模块,这是有原因的。 - Martijn Pieters
它对我有效,只需要做一次,永久解决! - GeekHades

3

我已经解决了这个问题,但让我困扰的是,如果将Unicode作为普通字符串输入,我的终端可以正确显示它,例如'уникоде',但如果我尝试将相同的字符串作为u'уникоде'打印,则会出现错误。 - disc0dancer

0

'абвгд' 不是一个 Unicode 字符串

u'абвгд' 是一个 Unicode 字符串

你不能直接打印 Unicode 字符串,必须先对其进行编码。在处理应用程序中的字符串时,你需要确保任何输入都被解码,任何输出都被编码。这样你的应用程序就只会在内部处理 Unicode 字符串,并以 UTF8 输出字符串。

供参考:

>>> 'абвгд'.decode('utf8') == u'абвгд'
>>> True

0

Unicode对象在显示在某些控制台之前需要进行编码。请尝试:

u'абвгд'.encode()

不要将Unicode编码为字符串对象(最可能使用utf8作为默认编码,但取决于您的Python配置)


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