python中的编码格式有哪些_python中编码格式

背景最近用python写一个在手机上安装、启动、卸载apk的脚本,在自己测试的时候用的是一个不带中文的apk,没有碰到什么问题,但是当真正去跑脚本的时候,突然报错了,查看错误信息,是 编码格式有问题。

所以就趁机把python的编码格式了解了一下。然后这里写篇文章来记录下解决问题的过程。

这里用的python版本是2.x,3.x对编码这一块有优化。

常见的编码格式

ASCII码计算机内部,所有的信息最终都表示为一个二进制的字符串,每一个二进制位(bit)有0和1两种状态,因此八个二进制位(也就是一个字节)可以组合出256中状态,上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定,这种被称为ASCII码,一直沿用至今。

ASCII码一共规定了128个字符的编码,比如空格“SPACE”是32(二进制00100000),这128个字符(包括32个不能打印出来的控制符号)只占用了一个字节的后面7位,最前面的1位统一是0。

非ASCII编码

英语用128个字符就够了,但是用来表示其他语言,128个字符就不够了,于是一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号,这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。

但是,又出现新的问题,不同国家有不同的字母,因此,哪怕他们都使用256个符号的编码方式,代表的字母却不一样,比如同样的都是130,但是法语、希伯来语、俄语代表的字母是不一样的,但是不管怎么样,所有这些编码方式中,0–127表示的字符是一样的,不一样的只是128–255的这一段。

至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右,一个字节只能表示256种符号,肯定是不够的,就必须使用多个自己表达一个字符,比如,简体中文常见的编码方式是GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示256*256=65536个符号。

这里需要指出,虽然都是用多个字节表示一个符号,但是GB类的汉字编码与后文的Unicode和UTF-8是毫无关系的。

UnicodeUnicode是一个很大的集合,是一种所有符号的编码,现在的规模可以容纳100多万个符号,每个符号的编码都不一样。

需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码如何存储,比如,汉字的‘严’的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号表示至少需要两个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。

这就有两个严重的问题,第一个问题是:如何才能区别Unicode和ASCII?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是:如果英文字母用一个字节表示就够了,如果Unicode统一规定,每隔符号用三个或4个字节表示,那么每个英文字母前都必然有二到三个字节是0,这样对于存储是极大的浪费。

然后出现的结果是:1)出现了Unicode的多种存储方式,也就是说许多种不同的二进制格式,可以用来表示Unicode。 2)Unicode在很长一段时间无法推广,直到互联网的出现。

UTF-8互联网的普及,强烈要求出现一种统一的编码格式。UTF-8就是在互联网上使用最广的一种Unicode的实现方式,其他的实现方式还包括UTF-16(字符用两个字节或4个字节表示)和UTF-32(字符用四个字节表示),需要注意的是这里的关系是,UTF-8是Unicode的实现方式之一。

UTF-8最大的特点是:它是一种变长的编码方式,它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。它的编码规则很简单,有两条:

对于单字节的符号,字节的第一位设为0,后面7位这个符号的Unicode码,因此对于英文字母,UTF-8编码和ASCII码是相同的。

对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10,剩下的没有提及的二进制位,全部为这个符号的Unicode码。

下图总结了编码规则,字母x表示可用编码的位。

跟据上表,解读UTF-8编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

举个例子,”严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此”严”的UTF-8编码需要三个字节,即格式是”1110xxxx 10xxxxxx 10xxxxxx”。然后,从”严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,”严”的UTF-8编码是”11100100 10111000 10100101”,转换成十六进制就是E4B8A5。可以结合着上图更好理解。

Little endian和Big endian上面提到,Unicode码可以采用UCS-2格式直接存储,以汉字“严”为例,Unicode码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25,存储的时候4E在前,25在后,就是Big Endian方式,25在前,4E在后就是Little endian方式。

那么计算机是怎么知道某一个文件到底采用哪一种方式编码? Unicode规范中定义,每一个文件的最前面分别加入一个标识编码顺序的字符,这个字符的名字叫做“零坎都非换行空格”用FEFF表示,这正好是两个字节,FF比FE大1,如果一个文本文件的头两个字节是FEFF,就表示该文件采用大头方式,如果头两个字节是FFFE,就表示该文件采用小头方式。

GBK && GB2312GB2312是国家制定的汉字编码标准,使用双字节编码,共收入6763个汉字和682个非汉字图形字符,GBK即对国际编码的扩展,在GB2312的基础上进行扩展形成的,使用双字节编码方式,共收入21003个汉字,从而满足了汉字使用的需要。

开始

简介字符串是python中最常见的数据类型,很多时候会用到一些不属于标准ASCLL字符集的字符,这时候代码很可能会抛出UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xc4 in position 10: ordinal not in range(128)异常,这是一个让初学者头疼的问题。

字符串在python内部的标识是Unicode编码,因此,在做编码转换时,通常需要以unicode作为中间编码,也就是说先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码。但是,python2.x的默认编码格式是ASCLL,因此在没有指定python源码编码格式的情况下,源码中所有字符都会被默认为ASCLL码,也因为这个根本原因,在python2.x中经常会遇到UnicodeDecodeError或者UnicodeEncodeError。

关于UnicodeUnicode是一种字符集,它为每一种现代或古代使用的文字系统出现的每一个字符都提供了统一的序列号,规定了符号的二进制代码,但没有规定这个二进制代码如何存储,也就是说:Unicode的编码方式是固定的,但是实现方式根据不同的需要有很多种,常见的有UTF-8、UTF-16、UTF-32等。

为了能够处理Unicode数据,同时兼容python某些内部模块,python 2.x中提供了Unicode这种数据类型,通过decode和encode方法可以将其他编码和Unicode编码相互转化,但同时也引入了UnicodeDecodeError和UnicodeEncodeError异常。

常见的几种编码异常

SyntaxError: Non-ASCII character这种异常最不容易出现,也最容易处理,主要原因是Python源码文件中有非ASCII字符,而且同时没有声明源码编码格式,例如:

1

2s = '中文'

print s # 抛出异常

UnicodeDecodeError这个异常有时候会在调用decode方法时出现,原因是Python打算将其他编码的字符转化为Unicode编码,但是字符本身的编码格式和decode方法传入的编码格式不一致,例如:

1

2

3

4

5#!/usr/bin/python

# -*- coding: utf-8 -*-

s = '中文'

s.decode('gb2312') # UnicodeDecodeError: 'gb2312' codec can't decode bytes in position 2-3: illegal multibyte sequence

print s

上面这段代码中字符串s的编码格式是utf-8,但是在使用decode方法转化为Unicode编码时传入的参数是‘gb2312’,因此在转化的时候抛出UnicodeDecodeError异常。还有一种情况是在encode的时候:

1

2

3

4

5#!/usr/bin/python

# -*- coding: utf-8 -*-

s = '中文'

s.encode('gb2312') # UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

print s

UnicodeEncodeError错误的使用decode和encode方法会出现这种异常,比如:使用decode方法将Unicode字符串转化的时候:

1

2

3

4

5#!/usr/bin/python

# -*- coding: utf-8 -*-

s = u'中文'

s.decode('utf-8') # UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

print s

上面这个错误可以这么理解:s是一个Unicode的字符串,然后想decode成utf-8格式的,这显然不合理。

解决方法

遵循PEP0263原则,声明编码格式在PEP 0263 – Defining Python Source Code Encodings中提出了对Python编码问题的最基本的解决方法:在Python源码文件中声明编码格式,最常见的声明方式如下:

1

2#!/usr/bin/python

# -*- coding: -*-

使用 u’中文’ 替代 ‘中文’1

2str1 = '中文编码'

str2 = u'中文编码'

Python中有以上两种声明字符串变量的方式,它们的主要区别是编码格式的不同,其中,str1的编码格式和Python文件声明的编码格式一致,而str2的编码格式则是Unicode。

如果你要声明的字符串变量中存在非ASCII的字符,那么最好使用str2的声明格式,这样你就可以不需要执行decode,直接对字符串进行操作,可以避免一些出现异常的情况。

Reset默认编码Python中出现这么多编码问题的根本原因是Python 2.x的默认编码格式是ASCII,所以你也可以通过以下的方式修改默认的编码格式:

1

2import sys

sys.setdefaultencoding('utf-8')

这种方法是可以解决部分编码问题,但是同时也会引入很多其他问题,得不偿失,不建议使用这种方式。

终极原则:decode early, unicode everywhere, encode late最后分享一个终极原则:decode early, unicode everywhere, encode late,即:在输入或者声明字符串的时候,尽早地使用decode方法将字符串转化成unicode编码格式;

然后在程序内使用字符串的时候统一使用unicode格式进行处理,比如字符串拼接、字符串替换、获取字符串的长度等操作;

最后,在输出字符串的时候(控制台/网页/文件),通过encode方法将字符串转化为你所想要的编码格式,比如utf-8等。

Python 3.x中的Unicode在Python 3.0之后的版本中,所有的字符串都是使用Unicode编码的字符串序列,同时还有以下几个改进:

默认编码格式改为unicode

所有的Python内置模块都支持unicode

不再支持u’中文’的语法格式

所以,对于Python 3.x来说,编码问题已经不再是个大的问题,基本上很少遇到上述的几个异常。

+++++++++

你可能感兴趣的:(python中的编码格式有哪些)