不管你是多年经验的Python老司机,还是Python新手,都一定遇到过UnicodeEncodeError和UnicodeDecodeError错误。每当遇到错误,我们就用encode和decode函数一遍又一遍的转换,有时候试一试就能解决问题,有时候怎么试都不行,只能借谷歌大神的帮助了,但你似乎很少关心问题的本质。下次再遇到类似的问题,重蹈覆辙,你觉得一次把Python字符编码搞清楚怎么样?在完全理解字符编码和Python的起源之前,我们需要弄清楚一些基本概念。有些概念虽然我们每天都会接触甚至用到,但我们并不一定了解它们。例如:字节、字符、字符集、字符编码、字符编码。字节是计算机中存储数据的基本单位。一个字节等于一个8位的位。计算机中的所有数据,无论是存储在磁盘文件中还是在网络上传输(文本、图片、视频、音频文件)都是由字节组成的。字符你正在阅读的文章是由许多字符(Character)组成的。字符是一个信息单元,是各种字符和符号的总称。比如英文字母是字符,汉字是字符,标点符号也是字符。字符集字符集(CharacterSet)是一定范围内的字符集合。不同的字符集规定了字符数。例如,ASCII字符集共有128个字符,包括英文字母、阿拉伯数字、标点符号和控制符号。GB2312字符集定义了7445个字符,包括大部分汉字。字符编码字符编码(CodePoint)是指字符集中每个字符的编号。例如,ASCII字符集使用0-127之间的128个连续数字来表示128个字符。例如“A”的字符编码编号为65。常见的字符编码有ASCII编码、UTF-8编码、GBK编码。从某种意义上说,字符集和字符编码之间存在对应关系,例如ASCII字符集对应ASCII编码。ASCII字符编码规定所有字符都使用单个字节的低7位进行编码。比如“A”的序号是65,单字节是0×41,所以写入存储设备时是b'01000001'。编码与解码编码的过程是将字符转换为字节流,解码的过程是将字节流解析为字符。了解了这些基本的术语概念之后,我们就可以开始讨论计算机字符编码的演变了。从ASCII码到字符编码,要从计算机的诞生说起。计算机是在美国发明的。在英语世界,常用的字符非常有限,26个字母(大小写),10个数字,标点符号,control这些字符在计算机中用一个字节的存储空间来表示绰绰有余,因为一个字节相当于8个比特,8个比特可以表示256个符号。于是美国国家标准协会ANSI制定了一套字符编码标准叫做ASCII(AmericanStandardCodeforInformationInterchange),每个字符对应一个唯一的数字,比如字符“A”对应数字65,而“B”"对应于66,依此类推。最早的ASCII只定义了128个字符代码,包括96个字符和32个控制符号。一共128个字符只需要一个字节的7位就可以表示所有的字符,所以ASCII只用一个字节的后7位。位,其余1位在某些通信系统中用作奇偶校验。下图是ASCII码字符码ExtendedASCII:EASCII(ISO/8859-1)的十进制、二进制和字符的对应表,但是当计算机慢慢普及到其他西欧地区时,发现还是有很多作为ASCII字符的西欧字符集中没有一个,显然ASCII已经不能满足人们的需求了。好在ASCII字符只使用字节0×00~0x7F的7位,一共128个字符,所以他们在ASCII的基础上将原来的7位扩展为8位,使用0×80-0xFF后面的128位数字,称为EASCII,与ASCII完全兼容,扩展符号包括表格符号、计算符号、希腊字母和特殊拉丁符号。然而,EASCII时代是一个混乱的时代。每个厂家都有自己的想法,大家没有统一的标准。他们各自根据自己的标准执行自己的一套字符编码标准。比较有名的是CP437。CP437是老祖宗IBMPC和MS-DOS使用的字符编码,如下图所示:很多ASCII扩展字符集互不兼容,让人无法正常交流。例如CP437字符集中200表示的字符是è,而在ISO/8859中╚在-1字符集中显示,所以一系列8位字符集标准ISO/8859-1(Latin-1)由国际标准化组织(ISO)和国际电工委员会(IEC)共同制定,继承了CP437字符编码的128-159之间的字符,所以从160开始定义,ISO-8859-1重新定义了160-255之间的字符在CP437的基础上。多字节字符编码GBKASCII字符编码是一种单字节编码。计算机进入中国后面临的问题之一就是如何处理汉字。对于拉丁语国家来说,通过扩展最高位,单个字节表示所有字符绰绰有余,但对于亚洲国家来说,一个字节似乎被拉长了。于是中国人制定了一套双字节字符编码,叫作GB2312,又称GB0,由中国国家标准局于1981年发布。GB2312编码共包含6763个汉字。同时,它还兼容ASCII。GB2312的出现基本满足了汉字计算机处理的需要。它收录的汉字已经覆盖了中国大陆99.75%的使用频率,但GB2312还不能100%满足汉字的需要,GB2312无法处理一些生僻字和繁体字。后来在GB2312的基础上又产生了GBK编码。GBK不仅收录了27484个汉字,还收录了藏文、蒙古文、维吾尔文等主要少数民族文字。同样,GBK也兼容ASCII编码。英文字符用1个字节表示,汉字用两个字节标识。Unicode和GBK的出现只是解决了我们自己的问题,但电脑不仅是美国人和中国人用的,欧洲和亚洲其他国家也有写的,比如日文和韩文。这已经大大超出了ASCII码乃至GBK所能表达的范围。虽然每个国家都可以制定自己的编码方案,但是在不同国家传输数据时会出现各种乱码。如果只用一种字符编码就可以表示地球甚至火星上的任何一个字符,问题就迎刃而解了。就是它,就是它,就是它,我们的小英雄,Unicode国际组织提出了Unicode编码,Unicode的学名是“UniversalMultiple-OctetCodedCharacterSet”,简称UCS。它为世界上每一种语言中的每个字符定义了唯一的字符代码。Unicode标准使用十六进制数字,数字前加前缀U+。例如,字母“A”的Unicode编码是U+0041。汉字“中”的Unicode编码为U+4E2DUnicode有两种格式:UCS-2和UCS-4。UCS-2是用两个字节编码的,共16位,所以理论上最多可以表示65536个字符,但是要表示世界上所有的字符来显示65536个数字是远远不够的,因为光是汉字就有近100,000,所以Unicode4.0规范定义了一组额外的字符编码。UCS-4使用4个字节(实际只用了31位,最高位必须为0)。理论上,它可以完全覆盖所有语言使用的符号。Unicode的局限性但是Unicode有一定的局限性。当一个Unicode字符在网络上传输或最终存储时,并不一定需要每个字符有两个字节。例如,字符“A”可以使用一个字节。表示的字符,但还是需要两个字节,显然是浪费空间。第二个问题是一个Unicode字符在计算机中保存的时候是一串01的数字,那么计算机怎么知道一个2字节的Unicode字符代表一个2字节的字符呢,比如Unicode编码的“中文”字符是U+6C49,我可以用4个ascii码来传输保存这个字符;我也可以用utf-8编码的3个连续字节E6B189来表示。关键是沟通双方必须同意。因此,Unicode编码有不同的实现方式,如:UTF-8、UTF-16等。Unicode就像英语。它是国家间通信的通用标准。每个国家都有自己的语言。他们将标准的英语文件翻译成自己国家的文本。这是实现方式,就像utf-8一样。具体实现:UTF-8UTF-8(UnicodeTransformationFormat)作为Unicode的一种实现,在互联网上被广泛使用。是一种变长字符编码,可以根据具体情况用1-4个字节来表示。特点。例如,可以用ASCII码表示的英文字符用UTF-8表示时,只需要一个字节的空间,与ASCII相同。对于多字节(n字节)的字符,第一个字节的前n位全部设为1,第n+1位设为0,后面字节的前两位全部设为10。剩余的二进制位全部用字符的unicode代码填充。以“good”为例,“good”对应的Unicode为597D,对应区间为00000800--0000FFFF,所以用UTF-8表示时需要存储3个字节,二进制表示597D的是:0101100101111101,补1110xxxx10xxxxxx10xxxxxx得到111001011010010110111101,转成十六进制是e5a5bd,所以“好”的Unicode编码U+597D对应的UTF-8编码是“E5A5BD”。您可以使用Python代码验证这一点:>>>a=u"good">>>au'u597d'>>>b=a.encode('utf-8')>>>len(b)3>>>b'xe5xa5xbd'现在终于理论完了。下面说说Python中的编码问题。Python的诞生比Unicode早得多。Python2的默认编码是ASCII。正因为如此,它导致了许多编码问题。>>>importsys>>>sys.getdefaultencoding()'ascii'所以在Python2中,源代码文件必须明确指定编码类型,否则代码中只要有中文就会报语法错误#coding=utf-8orYes:#--coding:utf-8--Python2charactertype在python2中,与字符串相关的数据类型有两种:str和unicode,继承自basestring,str类型字符串的编码格式可以可以是ascii、utf-8、gbk等任何类型。对于汉字“好”,用str表示时,其对应的utf-8编码为'xe5xa5xbd',对应的gbk编码为'xbaxc3',用unicode表示时,其对应的符号为u'u597d',这与u"Good"是等价的。str和unicode之间的转换在Python中str和unicode之间如何转换?这两类字符串之间的转换依赖于decode和encode这两个函数。encode负责将unicode编码成指定的字符编码,用于存储到磁盘或传输到网络。decode方法在应用程序中按照指定的编码方式解码后使用。_#__使用encode_从unicode转换为str>>>b=u'good'>>>c=b.encode('utf-8')>>>type(c)`
