关于python的编码,尤其是中文编码,会给新手带来很大的困扰。我以前遇到问题时搜索一下,虽然磕磕碰碰能解决并且顺利输出,但是背后的逻辑并未从根本上搞懂。
现在此仔细的整理一下,希望能让有同样疑惑的朋友有所收获。如有错误,也希望大家批评指正。
另外,这里针对的是python 2.7。python 3又有一些不同。
1、简单说一下字符编码
字符编码简单来讲就是转意,扩充基本字符的含义。
2、python字符串的类型
python字符串的类型有两种,一种是str,一种是unicode。这两者都是继承自抽象类basestring。
str是默认的字符串类型,本质上是8-bit string。构造方式如下
#默认的字符串就是str类型
s = '123'
print type(s)
#输出 <type 'str'>
#使用构造函数
s = str(123)
print type(s)
#输出 <type 'str'>
str类型的默认编码是ascii,因此默认情况下str不能表示中文,或者说在默认情况下py文件里都不能写中文
str1='中国'
#报错
#SyntaxError: Non-ASCII character '\xe4' in file C:\Users\Administrator\Desktop\1.py on line 1, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
对此的解决方法是更改str类型的编码集,使用utf-8。这需要在文件头进行声明(声明方式有多种,这里举一例)
# -*- coding: UTF-8 -*-
str1='中国'
print str1
#输出 中国
这里再介绍repr函数,以深化理解。repr()作用是将一个对象转成字符串显示,因此repr('中国')会显示真正存储的字符,而无视编码规则
# -*- coding: UTF-8 -*-
str1='中国'
print repr(str1)
#输出 '\xe4\xb8\xad\xe5\x9b\xbd'
下面介绍unicode类型。
unicode对象是无编码的字符串,或者说使用的是一套通用的字符编码集。python的unicode对象和真正的unicode编码其实不尽相同,但是我们不用考虑。
作为编码方式的unicode,中文叫万国码,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。unicode只是一种笼统的规范,具体的实现方式有utf-8、utf-16等。中文常用的就是utf-8编码,即8-bit Unicode Transformation Format,是可变长度的字符编码。
unicode的构造方式如下
#构造函数
u = unicode(123)
print type(u)
#输出 <type 'unicode'>
#或者用u+字符串方式
u = u'123'
print type(u)
#输出 <type 'unicode'>
unicode的作用是统一各种编码,其通用性可以避免不同编码方式会带来的错误。python包如json和urllib2,其请求的发送或返回值都是unicode编码。
unicode输出中文的方式就是重新编码,方法如下
# -*- coding: UTF-8 -*-
#使用encode函数进行编码,编码集为utf-8
u = u'中国'
print u.encode('utf-8')
#输出 中国
#如果不使用,则报错
#这里尽管str类型的编码被改为了utf-8,对于print函数来说仍是ascii
print u
#UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
再介绍一种unicode输出中文的方式:统一指定unicode的编码
# -*- coding: UTF-8 -*-
import sys
reload(sys)
sys.setdefaultencoding( "utf-8" )
u = u'中国'
print u
#输出 中国
3、decode和encode
python提供了解码和编码的函数decode和encode,其逻辑如下:
str——经过str.decode()——得到解码的unicode对象
unicode——经过unicode.encode()——得到编码的str对象
# -*- coding: UTF-8 -*-
s = '123'
print type(s)
#输出 <type 'str'>
#根据s编码方式选择解码方式
u = s.decode('utf-8')
print type(u)
#输出 <type 'unicode'>
s = u.encode('utf-8')print type(s)
#输出 <type 'str'>
4、最后介绍另一个工具chardet
使用chardet包可以查看str类型的编码方式。可以通过easy_install下载。查看方式如下
# -*- coding: UTF-8 -*-
import chardet
s = '中国'
print chardet.detect(s)
#{'confidence': 0.7525, 'encoding': 'utf-8'}
#注意到这里有'confidence',说明了chardet.detect是通过字符编码推测得出结论的
s = '123'
print chardet.detect(s)
#{'confidence': 1.0, 'encoding': 'ascii'}
#尽管指定了str编码为utf-8,但是由于utf-8的0-127与ascii相同,这里故无法察觉
s = '中国'.decode('utf-8').encode('gb2312')
print chardet.detect(s)
#{'confidence': 0.7679697235616183, 'encoding': 'IBM855'}
#指定了不同的编码方式,可以识别出来。此外这说明了gb2312和IBM855之间具有联系
s = '123'.decode('utf-8').encode('gb2312')
print chardet.detect(s)
#{'confidence': 1.0, 'encoding': 'ascii'}
#这里与上面情况2原理相同
5、关于读文件
一般来说同一个文件的编码应该是同一套的,对于纯utf-8、纯gbk2312编码的文件,直接使用open()即可。注意open()函数返回的每一行都是str类型的对象。
如果你想用不同的编码处理文件,按照前文所述,可以对每一行使用decode+encode的方法,也可以引用codecs包,在读文件时使用 codecs.open(filename,'utf-8') 等方法统一解码,此时返回的每一行都是unicode对象,再分别编码即可。(这就不需要上代码例子了)
需要注意的是,windows的txt文件可能会使用一种叫ANSI的编码方式。对于这种方式编码的文件,其每一行用chardet检测都可能显示出不同的编码(以gb2312/gbk为主)。不要以为这就是混合编码,实际上简体中文的windows系统的ANSI编码就是gbk编码,一般来说使用gbk解码即可。如果中途遇到问题,只需要另存为TXT文件,右下角改变编码方式为UTF-8等即可。