Python 和 Linux locale 学习笔记

遇到一个问题

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又个初步调查。

linux encode

1. 首先什么是 unicode 和 utf-8

首先电脑存放的都是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

2. Linux 上的编码

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 上的编码

这里有个问题值得注意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

你可能感兴趣的:(Python,python,linux)