【5.7.1】Python2中使用中文字符
一、使用中文字符
在python源码中如果使用了中文字符,运行时会有错误,解决的办法是在源码的开头部分加入字符编码的声明
#!/usr/bin/env python
# -*- coding: gb18030 -*-
或者 # -*- coding: utf-8 -*-
上面的写法是为了美观,当然也可以这么写:
# code: UTF-8 #这个貌似不好用额
我用的是
#coding:UTF-8
常见编码介绍:
- GB2312编码:适用于汉字处理、汉字通信等系统之间的信息交换
- GBK编码:是汉字编码标准之一,是在 GB2312-80 标准基础上的内码扩展规范,使用了双字节编码
- ASCII编码:是对英语字符和二进制之间的关系做的统一规定
- Unicode编码:这是一种世界上所有字符的编码。当然了它没有规定的存储方式。
- UTF-8编码:是 Unicode Transformation Format - 8 bit 的缩写, UTF-8 是 Unicode 的一种实现方式。它是可变长的编码方式,可以使用 1~4 个字节表示一个字符,可根据不同的符号而变化字节长度。
Python Tutorial中指出,python的源文件可以编码ASCII以外的字符集,最好的做法是在#!行后面用一个特殊的注释行来定义字符集:
# -*- coding: encoding -*-
根据这个声明,Python会尝试将文件中的字符编码转为encoding编码,并且,它尽可能的将指定地编码直接写成Unicode文本。 注意,coding:encoding只是告诉Python文件使用了encoding格式的编码,但是编辑器可能会以自己的方式存储.py文件,因此最后文件保存的时候还需要编码中选指定的ecoding才行。
二、中文字符的存储
str = u"中文"
str
u'\xd6\xd0\xce\xc4'
str = "中文"
str
'\xd6\xd0\xce\xc4'
u"中文"只是声明unicode,实际的编码并没有变。这样子就发生变化了:
str = "中文"
str
'\xd6\xd0\xce\xc4'
str = str.decode("gb2312")
str
u'\u4e2d\u6587'
更进一步:
s = '中文'
s.decode('gb2312')
u'\u4e2d\u6587'
len(s)
4
len(s.decode('gb2312'))
2
s = u'中文'
len(s)
4
s = '中文test'
len(s)
8
len(s.decode('gb2312'))
6
s = '中文test,'
len(s)
10
len(s.decode('gb2312'))
7
可以看出,对于实际Non-ASCII编码存储的字符串,python可以正确的识别出其中的中文字符以及中文上下文中的标点符号。 **前缀“u”表示“后面这个字符串“是一个Unicode字符串”,**这仅仅是一个声明,并不表示这个字符串就真的是Unicode了;就好比某正太声称自己已满18岁,但实际上他的真实年龄并不确定,现在体育界年龄造假可不稀罕幺!
那么声明成u有什么作用呢?对于Python来说,只要你声明某字符串是Unicode,它就会用Unicode的一套机制对它进行处理。比方说,做字符串操作的时候会动用到内部的Unicode处理函数,保存的时候以Unicode字符(双字节)进行保存。等等。显而易见,对于一个实际上并不是Unicode的字符串,做Unicode动作的处理,是有可能会出问题的。u前缀只适用于你的字符串常量真的是Unicode的情况。
三、中文字符的IO操作
用python处理字符串很容易,但是在处理中文的时候需要注意一些问题。比如:
a = "我们是python爱好者"
print a[0]
只能输出“我”字的前半部分,要想输出整个的“我”字还需要:
b = a[0:2]
print b
才行,很不方便,并且当一段文本中同时有中英文如何处理?最好的办法就是转换为unicode。像这样:
c = unicode(a, "gb2312")
print c[0]
这个时候c的下标对应的就是每一个字符,不再是字节,并且通过len(c)就可以获得字符数!还可以很方便的转换为其他编码,比如转换为utf-8:
d = c.encode("utf-8")
但是如果想要在raw_input()时接收中文字符,又会遇到错误。主要是先需要对输入的中文字符进行unicode的解码,然后在进行中文字符集的编码就可以直接传到字符中去,并通过http发到网页上。这里的中文字符集编码可以是gbk,gb2312,gb18030都可以
text = raw_input( "Input the contents: " )
text = unicode( text, 'gbk' ).encode( 'gb18030' )
编码转换:
Python内部的字符串一般都是 Unicode编码。代码中字符串的默认编码与代码文件本身的编码是一致的。所以要做一些编码转换通常是要以Unicode作为中间编码进行转换的,即先将其他编码的字符串解码(decode)成 Unicode,再从 Unicode编码(encode)成另一种编码。
decode 的作用是将其他编码的字符串转换成 Unicode 编码,eg name.decode(“GB2312”),表示将GB2312编码的字符串name转换成Unicode编码
encode 的作用是将Unicode编码转换成其他编码的字符串,eg name.encode(”GB2312“),表示将GB2312编码的字符串name转换成GB2312编码 所以在进行编码转换的时候必须先知道 name 是那种编码,然后 decode 成 Unicode 编码,最后载encode 成需要编码的编码。当然了,如果 name 已经就是 Unicode 编码了,那么就不需要进行decode 进行解码转换了,直接用 encode 就可以编码成你所需要的编码。值得注意的是:对 Unicode 进行编码和对 str 进行编码都是错误的。
字符串在Python内部的表示是unicode编码,因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码。
<type ‘str’>
将字符串看作是字节的序列,而 <type ‘unicode >
’则将其看作是字符的序列,单个字符可能占用多个字节;字节相对于字符,其在存储层次中更低一些。
str转换为unicode要decode,可以这样想,因为要把字节序列解释成字符序列,字节序列是底层的存放方式,解码(decode)成更高层的字符以便使用;同理,unicode转换为str要encode,就象信息编码(encode)后才存储一样:
处理中文数据时最好采用如下方式:
- Decode early(尽早decode, 将文件中的内容转化成unicode再进行下一步处理)
- Unicode everywhere (程序内部处理都用unicode)
- Encode late (最后encode回所需的encoding, 例如把最终结果写进结果文件)
下面是一个简单的演示,用re库查询一个中文字符串并打印:
p = re.compile(unicode("测试(.*)", "gb2312"))
s = unicode("测试一二三", "gb2312")
for i in p.findall(s):
print i.encode("gb2312")
一二三
四、python写文件
由于内置函数 open() 打开文件时,read() 读取的是 str,读取后需要使用正确的编码格式进行 decode()。write() 写入时,如果参数是 Unicode,则需要使用你希望写入的编码进行 encode(),如果是其他编码格式的 str,则需要先用该 str 的编码进行 decode(),转成 Unicode 后再使用写入的编码进行 encode()。如果直接将 Unicode 作为参数传入 write() ,python 将先使用源代码文件声明的字符编码进行编码然后写入。
# coding: UTF-8
fp1 = open('test.txt', 'r')
info1 = fp1.read()
# 已知是 GBK 编码,解码成 Unicode
tmp = info1.decode('GBK')
fp2 = open('test.txt', 'w')
# 编码成 UTF-8 编码的 str
info2 = tmp.encode('UTF-8')
fp2.write(info2)
fp2.close()
获取编码的方式:
判断是 s 字符串否为Unicode,如果是返回True,不是返回False :
isinstance(s, unicode)
下面代码可以获取系统默认编码:
#!/usr/bin/env python
#coding=utf-8
import sys
print sys.getdefaultencoding()
我的方法:
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
从文本读进来的汉字必须decode一下
status =seqs_status_info[seq_name]['status'].decode('utf-8')
五、删掉unicode 字符串前面的 u
有时我们会碰到类似下面这样的 unicode 字符串:
u'\xe4\xbd\xa0\xe5\xa5\xbd'
那如何才能得到我们想要的 \xe4\xbd\xa0\xe5\xa5\xbd 呢? python 提供了一个特殊的编码( raw_unicode_escape )用来处理这种情况:
In [4]: u'\xe4\xbd\xa0\xe5\xa5\xbd'.encode('raw_unicode_escape')
Out[4]: '\xe4\xbd\xa0\xe5\xa5\xbd'
In [5]: u'\xe4\xbd\xa0\xe5\xa5\xbd'.encode('raw_unicode_escape').decode('utf8')
Out[5]: u'\u4f60\u597d'
In [7]: print u'\u4f60\u597d'
你好
六、小结
一个比较一般的Python中文处理的流程:
- 将欲处理的字符串用unicode函数以正确的编码转换为Unicode
- 在程序中统一用Unicode字符串进行操作
- 输出时,使用encode方法,将Unicode再转换为所需的编码
有几点要说明一下:
- 所谓“正确的”编码,指得是指定编码和字符串本身的编码必须一致。这个其实并不那么容易判断,一般来说,我们直接输入的简体中文字符,有两种可能的编码:GB2312(GBK、GB18030)、以及UTF-8
- encode成本地编码的时候,必须要保证目标编码中存在欲转换字符的内码。encode这种操作一般是通过一个本地编码对应Unicode的编码转换表来进行的,事实上每个本地编码只能映射到Unicode的一部分。但是映射的区域是不同的,比如Big-5对应的Unicode的编码范围和 GBK对应的就不一样(实际上这两个编码有部分范围是重叠的)。所以,Unicode的一些字符(比如本身就是从GB2312转换来的那些),可以映射到 GBK,但未必可以映射到Big-5,如果你想转换到Big-5,很有可能就会出现编码找不到的异常。但UTF-8的码表范围实际上和Unicode是一样的(只是编码形式不同而已),所以,理论上来说,任何本地编码的字符,都可以被转换到UTF-8
- GB2312、GBK、GB18030本质上是同一种编码标准。只是在前者的基础上扩充了字符数量
- UTF-8和GB编码不兼容
七、报错
- UnicodeEncodeError: ‘ascii’ codec can’t encode character u'\u3000' in position 0: ordinal not in range(128)
解决办法:
#coding:UTF-8
tmplt = "{0:^10}\t{1:{3}^10}\t{2:^10}"
print tmplt.format('排名',"学校名称","总分",unichr(12288).encode('utf-8'))
报错2
在python2.7下,因为想从数据库中读出来分类名进行写入到文件,提示
Traceback (most recent call last):
File "test.py", line 28, in <module>
fp.write("%d:%s\r\n"%(sClassid,sClassName))
UnicodeEncodeError: 'ascii' codec can't encode character u'\uff08' in position 12: ordinal not in range(128
不用fp.write,用print打印却正常,这到底是怎么回来呢?
#! /usr/bin/python
# -*- coding: utf-8 -*-
import sys
print sys.getdefaultencoding();
运行上面的程序提示
ascii
解决办法:
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
再次运行,错误消息。
总结一下,python2.7是基于ascii去处理字符流,当字符流不属于ascii范围内,就会抛出异常(ordinal not in range(128))。
参考资料
个人公众号,比较懒,很少更新,可以在上面提问题,如果回复不及时,可发邮件给我: tiehan@sina.cn