python 中定义了unicode字符串。
server A
unicode_string=u"hello \u2026"
print unicode_string
server A output
hello …
server B
unicode_string=u"hello \u2026"
print unicode_string
server B output
Traceback (most recent call last):
File "" , line 1, in
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2026' in position 6: ordinal not in range(128)
这个问题让我对Linux encode和Python encode又个初步调查。
首先电脑存放的都是byte,ascii code 将符号,字符以一个byte来表示。8个‘0‘,‘1‘ 不同顺序排列出不同的字符和符号。
但是,一个byte并不能将世界上不同国家的文字和符都囊括在内。这时就出现了各种个样的编码标准。
最为普遍的编码标准是unicode, unicode 简单的来说是一种编码标准。它把一个文字,符号按照最多4个byte来编码。
但是编码后的字符或者符号电脑怎么认识呢(电脑只认识byte)?所以人们又发明了utf-8这种编码格式。unicode以一个统一的编码规范编程多个不同数量的byte,同时电脑以这个统一的规范读取和打印。
有篇简单易懂的好文大家看看:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
Linux 上有个查看本地话的工具:locale
locale 命令 可以查看所有本地化设置, 这里我分别在serverA和serverB上执行了locale。
发现serverA和serverB配置如下。
server A locale
VirtualBox:~$ locale
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=en_US.UTF-8
VirtualBox:~$ locale charmap
UTF-8
server B locale
VirtualBox:~$ locale
LANG=
LANGUAGE=
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_PAPER="C"
LC_NAME="C"
LC_ADDRESS="C"
LC_TELEPHONE="C"
LC_MEASUREMENT="C"
LC_IDENTIFICATION="C"
LC_ALL=C
VirtualBox:~$ locale charmap
ANSI_X3.4-1968
这里解释下这里的部分关键参数:
LANG="en_US.UTF-8" # 它的值用于指定下面环境变量没有设置的所有变量值。如果指定了上面任何一个变量的值,则会废除对应的LANG值的缺省设置。
LC_CTYPE="zh_CN.UTF-8" # 用于字符分类和字符串处理,控制所有字符的处理方式,包括字符编码,字符是单字节还是多字节,如何打印等。
LC_NUMERIC="en_US.UTF-8" # 指定使用某区域的非货币的数字格式
LC_TIME="en_US.UTF-8" # 指定使用某区域的日期和时间格式
LC_COLLATE="en_US.UTF-8" # 指定使用某区域的排序规则
LC_MONETARY="en_US.UTF-8" # 指定使用某区域的货币格式
LC_MESSAGES="en_US.UTF-8" # 用于控制程序输出时所使用的语言,主要是提示信息,错误信息,状态信息, 标题,标签, 按钮和菜单等。
LC_PAPER="en_US.UTF-8" # 指定使用某区域的纸张大小
LC_NAME="en_US.UTF-8" # 指定使用某区域的姓名书写方式
LC_ADDRESS="en_US.UTF-8" # 指定使用某区域的地址格式和位置信息
LC_TELEPHONE="en_US.UTF-8" # 指定使用某区域的电话号码格式
LC_MEASUREMENT="en_US.UTF-8" # 指定使用某区域的度量衡规则
LC_IDENTIFICATION="en_US.UTF-8" # 对 locale 自身信息的概述
LC_ALL= # 它不是环境变量,只是一个宏,可使用setlocale设置所有的LC_*环境变量。这个变量设置之后,可以废除LC_*和LANG的设置值,使得这些变量的设置值与LC_ALL的值一致。
当一个程序找环境变量值时,按照下面优先级
[1] LANGUAGE
[2] LC_ALL
[3] LC_xxx
[4] LANG
这里有个charmap,charmap这个文件定义了Locale中所有字符与内码的对应关系。通常是保存在系统的/usr/share/i18n/charmaps目录下(定义该Locale所支持的字符集中的每个字符)。比如说 2026是unicode省略号的意思如果charmap指定的对应关系中包涵2026和内码的对应关系则2026可以被解析。如果不包含则解析不了(UTF-8 中包涵,而ANSI_X3.4-1968中不包含并不能对unicdoe解码)。
所以上面两个服务器不同的结果很容易看出是为什么。此时看以用
python -c “import sys; print sys.stdout.encoding” 查看charmp的值。因为ANSI_X3.4-1968不能对nuicode 编码解码导致。相应的值打印不出来。
1.这里大家可以显示地设置Python的编码PYTHONIOENCODING。
在 ViM 中运行 :!python -c “import sys; print(sys.stdout.encoding)” 时,输出可能是
ANSI_X3.4-1968 (即使你设置了正确的Locale) . 把 PYTHONIOENCODING 变量设置成 utf-8
可以规避这个问题.
2.大家可以在程序中显示的加上encode(‘utf-8’)
unicode_string=u"hello \u2026".encode('utf-8')
3.大家可以设置locale
LC_ALL=en_US.utf8
export LC_ALL
这里有些参考性的资料大家可以看下
https://wiki.archlinux.org/index.php/Locale_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)
https://www.ibm.com/developerworks/cn/linux/l-cn-linuxglb/
https://www.ibm.com/developerworks/cn/linux/i18n/unicode/linuni/
http://www.programgo.com/article/41343600592/
https://linux.cn/lfs/LFS-BOOK-7.7-systemd/chapter07/locale.html
这里有个问题值得注意Python报的错误和ANSI_X3.4-1968 一点关系也没有。而是一个’ascii’问题。
这里就涉及到了Python的编码。
import sys
import locale
#当unicode字符做连接时,python会隐性的做encode,
#大概是Python 发现程序中有文字要和unicode做连接,而且也没有指定是unicode。python会尝试用getdefaultencoding进行编码后,在做处理。
#这里的encode的编码格式是有下面参数决定的。
sys.getdefaultencoding()
#eg 这里会给hello编码,编码格式由sys.getdefaultencoding()这个值决定。
u"\u2026" + "hello"
#if sys.getdefaultencoding() is ascii, this cause error
#这里‘中文‘不能用ascii编码,会报错。要指明中文是unicode才行
print "中文"+u"\u2026"
Traceback (most recent call last):
File "" , line 1, in
UnicodeDecodeError: 'ascii' codec cant decode byte 0xe4 in position 0: ordinal not in range(128)
#指明中文是unicode,python知道‘中文‘不适ascii码,是unicode。所以不会再用默认格式编码。
a=u"中文"+u"\u2026"
print a
中文…
#this matter what is print out
#same with 'print(sys.stdout.encoding)'
#To control stdin.encoding and stdout.encoding you want to set PYTHONIOENCODING
# 这里的到系统locale的值,这里的值和charmap一样,
# 决定了你的"输出"和"输入"(both)。
lcoale.getpreferredencoding()
# eg
print u"\u2026"
我遇到的情况中,错误首先是被Python,捕捉到了,而且跑出了异常。
unicode_string=u"hello \u2026"
print unicode_string
Traceback (most recent call last):
File "" , line 1, in
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2026' in position 6: ordinal not in range(128)
这里指明了时unicode,但是打印出来报ascii错。这种状况和做链接时相似。原因可能是系统使用locale.getpreferredencoding() 或者(sys.stdout.encoding)设置的是ANSI_X3.4-1968 编码方式,这个编码方式并不能编码unicode。
这里报错的‘ascii‘并不是setdefaultencoding中的ascii
Python 2.7.6 (default, Jun 22 2015, 17:58:13)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print u"hello \u2026"
Traceback (most recent call last):
File "" , line 1, in
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2026' in position 6: ordinal not in range(128)
>>> import sys
>>> reload(sys)
sys' (built-in)>
>>> print u"hello \u2026"
Traceback (most recent call last):
File "", line 1, in
UnicodeEncodeError: ' ascii' codec can't encode character u'\u2026' in position 6: ordinal not in range(128)
>>> sys.setdefaultencoding('utf-8')
>>> print u"hello \u2026"
Traceback (most recent call last):
File "" , line 1, in
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2026' in position 6: ordinal not in range(128)
>>> sys.getdefaultencoding()
'utf-8'
那这个ascii是哪的呢?我暂时也不知道。求大神告知。
参考资料
python -c “import locale; print locale.getdefaultlocale()”
http://nedbatchelder.com/text/unipain.html
https://docs.python.org/2/howto/unicode.html