一直遇到python编码的问题。常常抓取的网页数据信息,邮件收发信息,涉及到文字处理的,都时不时出现编码的问题。终于,觉得有必要认真了解下这个编码过程和出现对应的问题如何解决,在网上找了很多解释和文章,整理了下,并结合实践了一些,记录在这里。
ASCII及其扩展字符集
作用:表语英语及西欧语言。
位数:ASCII是用7位表示的,能表示128个字符;其扩展使用8位表示,表示256个字符。
范围:ASCII从00到7F,扩展从00到FF。
ISO-8859-1字符集
作用:扩展ASCII,表示西欧、希腊语等。
位数:8位,
范围:从00到FF,兼容ASCII字符集。
GB2312字符集
作用:国家简体中文字符集,兼容ASCII。
位数:使用2个字节表示,能表示7445个符号,包括6763个汉字,几乎覆盖所有高频率汉字。
范围:高字节从A1到F7, 低字节从A1到FE。将高字节和低字节分别加上0XA0即可得到编码。
GB2312码是中华人民共和国国家汉字信息交换用码,1981年5月实施,通行于大陆。新加坡等地也是用此编码。
GB 2312规定任意一个图形字符都采用两个字节表示,每个字节均采用7位编码表示,习惯上称第一个字节为高字节,第二个字节为低字节。
GB 2312将代码分别表示为94个区,对应第一个字节;每个区94个位,对应第二个字节,两个字节的值分别为区号值和位号值加32(20H),因此也称为区位码。01—9为符号,数字区,16-87区为汉字区,10-15区,88-94区是有待进一步标准化的空白区。GB 2312最多能表示6763个汉字。编码范围为2121H-777EH,与ASCII有重叠,通行的方法是将GB码两个字节的最高位置1以示区别。
BIG-5 码是通行于台湾、香港地区的一个繁体字编码方案,俗称“大五码”。它并不是一个法定的编码方案,但它广泛地被应用于电脑业,尤其是国际互联网中,从而成为一种事实上的行业标准。它包括440个符号,一级汉字5401个,二级汉字7652个,共计13060个汉字。BIG5是一个双字节编码方案,其第一字节的值在16进制的A0-7E之间,第二字节在A1-FE之间。一次其第一个字节的最高位是1,第二字节的最高位可能是1,也可能是0。
GBK字符集
作用:它是GB2312的扩展,加入对繁体字的支持,兼容GB2312。
位数:使用2个字节表示,可表示21886个字符。
范围:高字节从81到FE,低字节从40到FE。
GBK 向下与GB 2312编码兼容,向上支持ISO 10646.1国际标准,是前者向后者过渡过程中的一个承上启下的标准,共收录汉字21003个,符号883个,并提供1894个造字码位,简,繁体字融为一库。
GB18030字符集
作用:它解决了中文、日文、朝鲜语等的编码,兼容GBK。
位数:它采用变字节表示(1 ASCII,2,4字节)。可表示27484个文字。
范围:1字节从00到7F; 2字节高字节从81到FE,低字节从40到7E和80到FE;4字节第一三字节从81到FE,第二四字节从30到39。
GB 18030编码标准由信息产业部和国家质量技术监督局在2000年3月日联合发布,并作为国家标准在2001年的1月强制执行。
GB 18030是在原来GB2312-1980标准和GBK编码标准的基础上进行的扩充,增加了四字节部分的编码。它可以完全映射ISO 10646的基本平面和所有辅助平面,共有150多万码位。在ISO 10646的基本平面内,它在原来的2万多汉字的基础上增加了7000多个汉字的码位和字形。它主要目的是为了解决一些生,难,偏字的问题,以及适应出版,邮政,户政,金融,地理信息系统等迫切需要的人名,地名用字问题。
GB 18030-2000编码标准收录的字符分别一单字节,双字节和四字节编码。
重点说下UTF-8,这个用的最多
UTF-8(8-bit UnicodeTransformation Format)是一种针对Unicode的可变长度字符编码,也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。ascii编码是UTF-8的子集。
UTF-8使用一至六个字节为每个字符编码:
UTF- 8是UNICODE的一种变长字符编码。现在已经标准化为RFC3629。UTF-8用1到6个字节 编码UNICODE字符。如果UNICODE字符由2个字节表示,则编码成UTF-8很可能需要3个字节,而如果UNICODE字符由4个字节表示,则编 码成UTF-8可能需要6个字节。在UTF-8中,如果一个字符的字节小于0x80(128)则为ASCII字符,占一个字节,可以不用转换,因为UTF-8兼容ASCII编码
下表所列的字符串用来表示一个字符。用到那个串取决于该字符在Unicode中的序号。
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx
Xxx的位置由字符编码数的二进制表示的位填入。越靠右的x具有越少的特殊意义。只用最短的那个足够表达一个字符编码的多字节串。注意在多字节串中,第一个字节的开头“1”的数目就是整个串中字节的数目。
例如:Unicode字符U+00A9=1010 1001在UTF-8里的编码为:
11000010 10101001 = 0xC2 0xA9
而字符 U+2260= 0010 0010 0110 0000编码为:
11100010 10001001 10100000 = 0xE2 0x89 0xA0
这种编码的官方名字拼写为 UTF-8,其中UTF代表UCSTransformation Format。
Definitions
• Unicode string: sequence of Unicode characters
• Unicode字符串:Unicode字符序列的集合
• Python bytestring: a series of bytes which represent a sequence of characters. It's default encoding is ASCII. This is the "normal", non-Unicode string in Python <3.0.
• Python字节串:一连串代表字符串序列的字节集合.默认以ascii编码.在python3.0以下,这是一种常见的非unicode字符串.
• encoding: a code that pairs a sequence of characters with a series of bytes
• 编码:使用一连串字节来表示字符序列对的符号.
• ASCII: an encoding which handles 128 English characters
• Ascii:一种处理128个英语字符的编码方式
• UTF-8: a popular encoding used for Unicode strings which is backwards compatible with ASCII for the first 128 characters. It uses one to four bytes for each character.
• UTF-8:一种用于向后兼容ASCII前128个字符的unicode 字符串的流行编码.每个字符由一到四个字节组成.
Operations related to str and unicode objects
• unicode.encode() - converts to str
• 转换为str类型
• str.decode() - converts to Unicode
• 转换为unicode类型
• unicode(str, encoding) - converts to Unicode
• 转换str为unicode类型
• ord(c) - returns the Unicode code point of the character
• 返回字符的unicode 编码编号
• chr(i) - returns a str object for the given ASCII code (inverse of ord() for 8-bit strings)
• 对给定的ascii编码返回一个str 对象(对8-bit的字符串进行ord()反转)
• unichr(i) - returns a unicode object for the given Unicode code (inverse of ord() for Unicode strings)
• 对给定的unicode 编码返回一个unicode 对象(对unicode 字符串进行ord()反转)
类型操作表:
Python有两种字符串str object 和 Unicode object, 都可以存放字符的字节编码,但是不同的类型。
转换方式:
Unicode ——》encode ——》得到str
Str ——》decode ——》得到 Unicode
decode的作用是将其他编码的字符串转换成unicode编码,如str1.decode('gb2312'),表示将gb2312编码的字符串转换成unicode编码。
encode的作用是将unicode编码转换成其他编码的字符串,如str2.encode('gb2312'),表示将unicode编码的字符串转换成gb2312编码。
Unicode编码是一种抽象编码,不能直接表示,需要转换为对应的编码才能保存到磁盘上。
函数 decode( char_set )可以实现 其它编码到 Unicode 的转换,函数 encode( char_set )实现 Unicode 到其它编码方式的转换。
除上以上的编码方法,在读写文件时还可以使用codecs的open方法在读写时进行转换
获取系统默认字符编码
>>> import sys
>>> print sys.getdefaultencoding()
ascii
1, 源文件编码
在文件头部使用coding声明。告诉python解释器该代码文件所使用的字符集。
#/usr/bin/python #coding: utf8
字符串在Python内部的表示是unicode编码,因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码
在windows的idle下
>>> s = u'我是中国人' >>> s u'\xce\xd2\xca\xc7\xd6\xd0\xb9\xfa\xc8\xcb' >>> type(s) <type 'unicode'> >>>
>>> t = '我是中国人' >>> t '\xce\xd2\xca\xc7\xd6\xd0\xb9\xfa\xc8\xcb' >>> type(t) <type 'str'>
>>> print t 我是中国人 >>> print s ÎÒÊÇÖйúÈË >>>
以文件方式运行:
#_*_encoding:utf-8 _*_ s = u'我是中国人' t = '我是中国人' print s print type(s) print t print type(t)
>>> ================================ RESTART ================================ >>> 我是中国人 <type 'unicode'> 我是中国人 <type 'str'> >>>
>>> s u'\xce\xd2\xca\xc7\xd6\xd0\xb9\xfa\xc8\xcb' >>> t '\xce\xd2\xca\xc7\xd6\xd0\xb9\xfa\xc8\xcb'根据转换方式:
转换方式:
Unicode ——》encode ——》得到str
Str ——》decode ——》得到 Unicode
先对unicode 类型的s进行编码:
>>> s.encode('utf-8') '\xc3\x8e\xc3\x92\xc3\x8a\xc3\x87\xc3\x96\xc3\x90\xc2\xb9\xc3\xba\xc3\x88\xc3\x8b'对str 类型进行解码,将gb2312编码的字符串转换为unicode编码
>>> t.decode('gb2312') u'\u6211\u662f\u4e2d\u56fd\u4eba'
则该字符串的编码就被指定为unicode了,即python的内部编码,而与代码文件本身的编码无关。因此,对于这种情况做编码转换,只需要直接使用encode方法将其转换成指定编码即可。
使用gb312作为代码文件编码
#_*_encoding:gb2312 _*_ s = u'我是中国人' t = '我是中国人' print s print type(s) print t print type(t) print s.encode('utf-8') print t.decode('gb2312')
>>> ================================ RESTART ================================ >>> 我是中国人 <type 'unicode'> 我是中国人 <type 'str'> 我是中国人 我是中国人 >>>使用utf-8作为代码文件编码
#_*_encoding:utf-8 _*_ s = u'我是中国人' t = '我是中国人' print s print type(s) print t print type(t) print s.encode('utf-8') print t.decode('utf-8')
>>> ================================ RESTART ================================ >>> 我是中国人 <type 'unicode'> 我是中国人 <type 'str'> 我是中国人 我是中国人在有文件编码的情况下str 也能进行decode输出可读文字了。
如果一个字符串已经是unicode了,再进行解码则将出错,因此通常要对其编码方式是否为unicode进行判断:
isinstance(s, unicode) #用来判断是否为unicode
print语句的实现是将要输出的内容传送给操作系统,操作系统会根据系统的编码对输入的字节流进行编码,这就解释了为什么utf-8格式的字符串
s = u'我是中国人' print s.encode('utf-8') 脦脪脢脟脰脨鹿煤脠脣,输出的是乱码,因为
>>> s u'\xce\xd2\xca\xc7\xd6\xd0\xb9\xfa\xc8\xcb'用GB2312去解释,其显示的出来就是“乱码”。这里再强调一下,str记录的是字节数组,只是某种编码的存储格式,至于输出到文件或是打印出来是什么格式,完全取决于其解码的编码将它解码成什么样子。
这里再对print进行一点补充说明:将一个unicode对象传给print时,在内部会将该unicode对象进行一次转换,转换成本地的默认编码。
1、获取当前Idle下的默认编码:
python 2.7.3 (default, Apr 202012, 22:44:07) [GCC 4.6.3] on linux2 Type "help","copyright", "credits" or "license" for moreinformation. >>> import sys >>> printsys.getdefaultencoding() Ascii
2、不同字符文件的写入
1)普通英文写入文件中
#!/bin/env python #coding:utf-8 newtext = 'this is a new linefor test!\n' fp = open('file.txt','w') fp.write(newtext.encode('utf-8')) fp.close()
英文写入正常如下:
lijy@loongson-desktop:~/python$python write_text.py lijy@loongson-desktop:~/python$cat file.txt this is a new line for test!
2)写入中文
#!/bin/env python #coding:utf-8 newtext = '我是中国人' fp = open('file.txt','w') fp.write(newtext.encode('utf-8')) fp.close()
报错:
lijy@loongson-desktop:~/python$python write_text.py Traceback (most recent calllast): File "write_text.py", line 8, in<module> fp.write(newtext.encode('utf-8'))UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)
修改为:去掉encode(’utf-8’),根据类型转换规则,str类型没有encode方法
#!/bin/env python #coding:utf-8 newtext = '我是中国人\n' fp = open('file.txt','w') fp.write(newtext) fp.close() 输出正常: lijy@loongson-desktop:~/python$python write_text.py lijy@loongson-desktop:~/python$cat file.txt 我是中国人结论:对非U开头的汉字不需要encode(’utf-8’)
3)写入中英文混合的内容
情况一:有encode(’utf-8’)
#!/bin/env python #coding:utf-8 newtext = 'This is a test, 我是中国人\n' fp = open('file.txt','w') fp.write(newtext.encode('utf-8')) fp.close() lijy@loongson-desktop:~/python$python write_text.py Traceback (most recent calllast): File "write_text.py", line 9, in<module> fp.write(newtext.encode('utf-8'))unicodeDecodeError: 'ascii'codec can't decode byte 0xe6 in position 16: ordinal not in range(128)
结果:报错不支持decode,根据转换规则,unicode 类型没有decode方法
情况二:不需要encode(’utf-8’)
#!/bin/env python #coding:utf-8 newtext = 'This is a test, 我是中国人\n' fp = open('file.txt','w') fp.write(newtext) fp.close() lijy@loongson-desktop:~/python$python write_text.py lijy@loongson-desktop:~/python$cat file.txt This is a test, 我是中国人 lijy@loongson-desktop:~/python$结果:中英文都输出正常
综上,对 str 类型的字符串,英文字符可以encoding(’utf-8’),或直接写入;汉字直接写入;中英文混合,直接写入;
假如去掉文本的编码方式呢?
#!/bin/env python报错没有指定编码格式,中文为非ASCII字符#coding:
newtext = 'This is a test, 我是中国人\n'
fp = open('file.txt','w')
fp.write(newtext)
fp.close()
lijy@loongson-desktop:~/python$python write_text.py
File "write_text.py", line 5
SyntaxError: Non-ASCII character '\xe6' in file write_text.py on line 5, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
将coding指定为ascii,gb2312也会出现对应的报错,原因主要可能是utf-8支持中英文,而ascii不支持中文,gb2312不支持英文
建立一个文件test.txt,文件格式用ANSI,内容为:
abc中文
用python来读取
# coding=gbk
print open("Test.txt").read()
结果:abc中文
把文件格式改成UTF-8:
结果:abc涓枃
显然,这里需要解码:
# coding=gbk
import codecs
print open("Test.txt").read().decode("utf-8")
结果:abc中文
上面的test.txt我是用Editplus来编辑的,但当我用Windows自带的记事本编辑并存成UTF-8格式时,
运行时报错:
Traceback (most recent call last):
File "ChineseTest.py", line 3, in
print open("Test.txt").read().decode("utf-8")
UnicodeEncodeError: 'gbk' codec can't encode character u'\ufeff' in position 0: illegal multibyte sequence
原来,某些软件,如notepad,在保存一个以UTF-8编码的文件时,会在文件开始的地方插入三个不可见的字符(0xEF 0xBB 0xBF,即BOM)。
因此我们在读取时需要自己去掉这些字符,python中的codecs module定义了这个常量:
# coding=gbk
import codecs
data = open("Test.txt").read()
if data[:3] == codecs.BOM_UTF8:
data = data[3:]
print data.decode("utf-8")
结果:abc中文
五、文件的编码格式和编码声明的作用
源文件的编码格式对字符串的声明有什么作用呢?这个问题困扰一直困扰了我好久,现在终于有点眉目了,文件的编码格式决定了在该源文件中声明的字符串的编码格式,例如:
str = '哈哈'
print repr(str)
a.如果文件格式为utf-8,则str的值为:'\xe5\x93\x88\xe5\x93\x88'(哈哈的utf-8编码)
b.如果文件格式为gbk,则str的值为:'\xb9\xfe\xb9\xfe'(哈哈的gbk编码)
在第一节已经说过,python中的字符串,只是一个字节数组,所以当把a情况的str输出到gbk编码的控制台时,就将显示为乱码:鍝堝搱;而当把b情况下的str输出utf-8编码的控制台时,也将显示乱码的问题,是什么也没有,也许'\xb9\xfe\xb9\xfe'用utf-8解码显示,就是空白吧。>_<
说完文件格式,现在来谈谈编码声明的作用吧,每个文件在最上面的地方,都会用# coding=gbk 类似的语句声明一下编码,但是这个声明到底有什么用呢?到止前为止,我觉得它的作用也就是三个:
#coding:gbk
ss = u'哈哈'
print repr(ss)
print 'ss:%s' % ss
将这个些代码保存成一个utf-8文本,运行,你认为会输出什么呢?大家第一感觉肯定输出的肯定是:
u'\u54c8\u54c8'
ss:哈哈
但是实际上输出是:
u'\u935d\u581d\u6431'
ss:鍝堝搱
为什么会这样,这时候,就是编码声明在作怪了,在运行ss = u'哈哈'的时候,整个过程可以分为以下几步:
1) 获取'哈哈'的编码:由文件编码格式确定,为'\xe5\x93\x88\xe5\x93\x88'(哈哈的utf-8编码形式)
2) 转成 unicode编码的时候,在这个转换的过程中,对于'\xe5\x93\x88\xe5\x93\x88'的解码,不是用utf-8解码,而是用声明编码处指定的编码GBK,将'\xe5\x93\x88\xe5\x93\x88'按GBK解码,得到就是''鍝堝搱'',这三个字的unicode编码就是u'\u935d\u581d\u6431',至止可以解释为什么print repr(ss)输出的是u'\u935d\u581d\u6431' 了。
好了,这里有点绕,我们来分析下一个示例:
#-*- coding:utf-8 -*- ss = u'哈哈' print repr(ss) print 'ss:%s' % ss将这个示例这次保存成GBK编码形式,运行结果,竟然是:
UnicodeDecodeError: 'utf8' codec can't decode byte 0xb9 in position 0: unexpected code byte
这里为什么会有utf8解码错误呢?想想上个示例也明白了,转换第一步,因为文件编码是GBK,得到的是'哈哈'编码是GBK的编码'\xb9\xfe\xb9\xfe',当进行第二步,转换成 unicode的时候,会用UTF8对'\xb9\xfe\xb9\xfe'进行解码,而大家查utf-8的编码表会发现,utf8编码表(关于UTF- 8解释可参见字符编码笔记:ASCII、UTF-8、UNICODE)中根本不存在,所以会报上述错误。
二、python源文件的编码与解码,我们写的python程序从产生到执行的过程如下:#coding=utf-8 try: JAP=open("e:/jap.txt","r") CHN=open("e:/chn.txt","r") UTF=open("e:/utf.txt","w") jap_text=JAP.readline() chn_text=CHN.readline() #先decode成UTF-16,再encode成UTF-8 jap_text_utf8=jap_text.decode("SHIFT_JIS").encode("UTF-8") #不转成utf-8也可以 chn_text_utf8=chn_text.decode("GB2312").encode("UTF-8")#编码方式大小写都行utf-8也一样 UTF.write(jap_text_utf8) UTF.write(chn_text_utf8) except IOError,e: print "open file error",e
http://www.360doc.com/content/11/0213/01/2563193_92593666.shtml
http://www.jb51.net/article/26543.htm
http://www.jb51.net/article/26541.htm
http://www.jb51.net/article/26542.htm