您的位置:首页 > 运维架构 > Linux

Python 和 Linux locale 学习笔记

2016-08-21 13:03 519 查看

遇到一个问题

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 "<stdin>", line 1, in <module>
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=                             # 它不是环境变量,
b47e
只是一个宏,可使用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 "<stdin>", line 1, in <module>
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 "<stdin>", line 1, in <module> 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 "<stdin>", line 1, in <module> UnicodeEncodeError: 'ascii' codec can't encode character u'\u2026' in position 6: ordinal not in range(128)
>>> import sys

>>> reload(sys)
<module 'sys' (built-in)>
>>> print u"hello \u2026"
Traceback (most recent call last): File "<stdin>", line 1, in <module> 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 "<stdin>", line 1, in <module> 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 linux
相关文章推荐