Python字符编码问题学习

时间:2023-01-05 19:15:01

1、字符编码简介:

1.1 ASCII

ASCII(AmericanStandard Code for Information Interchange),是一种单字节编码。计算机世界里一开始只有英文,而单字节可以表示256个不同的字符,可以表示所有的英文字符和许多的控制 符号。

1.2 MBCS

计算机中有了其他语言,不同语言使用了不同的编码方式,IBM发明了一个叫Code Page的概念,将这些编码进行分配页码,GBK是第936页,也就是CP936。所以,也可以使用CP936表示GBK。MBCS(Multi-ByteCharacter Set)是这些编码的统称。目前为止大家都是用了双字节,所以有时候也叫做DBCS(Double-ByteCharacter Set)。在windows中,ANSI就是MBCS。同时,在简体中文Windows默认的区域设定里,MBCS指代GBK。

1.3 Unicode

后来,有人开始觉得太多编码导致世界变得过于复杂了,让人脑袋疼,于是大家坐在一起拍脑袋想出来一个方法:所有语言的字符都用同一种字符集来表示,这就是Unicode。可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失

1.4 UTF-8

需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。这里就产生了两个严重的问题:1)计算机要如何区分Unicode编码和ASCII编码呢? 2)对于英文字母来说用Unicode编码时每个字符使用三个四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。


UTF-8就是在互联网上使用最广的一种Unicode的实现方式。其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一

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

 

2、字符与字节

2.1 字符与字节

一个字符不等价于一个字节,字符是人类能够识别的符号,而这些符号要保存到计算的存储中就需要用计算机能够识别的字节来表示。一个字符往往有多种表示方法,不同的表示方法会使用不同的字节数。这里所说的不同的表示方法就是指字符编码,比如字母A-Z都可以用ASCII码表示(占用一个字节),也可以用UNICODE表示(占两个字节),还可以用UTF-8表示(占用一个字节)。字符编码的作用就是将人类可识别的字符转换为机器可识别的字节码,解码就是讲机器可识别的字节码转换成人类可识别的字符。


UNICDOE才是真正的字符串,而用ASCII、UTF-8、GBK等字符编码表示的是字节串。从上面对各种编码方式的介绍中,我们也可以了解到,像ASCII、UTF-8、GBK这些都是编码方式,是将字符编码为字节码。Unicode只是一个符号集,它只规定了符号的二进制代码,也就是说他给每一个字符一个独一无二的数字来表示。

 

2.2 编码与解码

编码(encode):在Unicode中,每一个字符都有一个唯一的数字表示,那么将Unicode字符串转换为特定字符编码(ASCII、UTF-8、GBK)对应的字节串的过程和规则就是编码。

解码(decode):特定字符编码(ASCII、UTF-8、GBK)的字节串转换为对应的Unicode字符串的过程和规则就是解码。

 

3、Python中的默认编码

3.1 Python源代码文件的执行过程

我们都知道,磁盘上的文件都是以二进制格式存放的,其中文本文件都是以某种特定编码的字节形式存放的。对于程序源代码文件的字符编码是由编辑器指定的,比如我们使用Pycharm来编写Python程序时会指定工程编码和文件编码为UTF-8,那么Python代码被保存到磁盘时就会被转换为UTF-8编码对应的字节(encode过程)后写入磁盘。当执行Python代码文件中的代码时,Python解释器在读取Python代码文件中的字节串之后,需要将其转换为UNICODE字符串(decode过程)之后才执行后续操作。

Python字符编码问题学习

 

3.2  默认编码

那么,如果我们没有在代码文件开始的部分指定字符编码,Python解释器就会使用哪种字符编码把从代码文件中读取到的字节转换为UNICODE字符串呢?就像我们配置某些软件时,有很多默认选项一样,需要在Python解释器内部设置默认的字符编码来解决这个问题,这就是文章开头所说的“默认编码”。

Python2和Python3的解释器使用的默认编码是不一样的,我们可以通过  sys.getdefaultencoding() 来获取默认编码:

>>># Python2
>>>import sys
>>>sys.getdefaultencoding()
'ascii'

>>># Python3
>>>import sys
>>>sys.getdefaultencoding()
'utf-8'

因此,对于Python2来讲,Python解释器在读取到中文字符的字节码尝试解码操作时,会先查看当前代码文件头部是否有指明当前代码文件中保存的字节码对应的字符编码是什么。如果没有指定则使用默认字符编码"ASCII"进行解码导致解码失败,导致如下错误:

SyntaxError:Non-ASCII character '\xc4' in file xxx.py on line 11, but no encoding declared;see http://python.org/dev/peps/pep-0263/ for details

对于Python3来讲,执行过程是一样的,只是Python3的解释器以"UTF-8"作为默认编码,但是这并不表示可以完全兼容中文问题。比如我们在Windows上进行开发时,Python工程及代码文件都使用的是默认的GBK编码,也就是说Python代码文件是被转换成GBK格式的字节码保存到磁盘中的。Python3的解释器执行该代码文件时,试图用UTF-8进行解码操作时,同样会解码失败,导致如下错误:

SyntaxError:Non-UTF-8 code starting with '\xc4' in file xxx.py on line 11, but no encodingdeclared; see http://python.org/dev/peps/pep-0263/ for details

3.3 最佳实践

源代码文件中,如果有用到非ASCII字符,则需要在文件头部进行字符编码的声明,如下:

#-*-coding: UTF-8 -*-

告知python我这个文件里的文本是用utf-8编码的,这样,python就会依照utf-8的编码形式解读其中的字符,然后转换成unicode编码内部处理使用。

实际上Python只检查#、coding和编码字符串,其他的字符都是为了美观加上的。

 

4、Python2与Python3中对字符串的支持

 

4.1 Python2

Python2中对字符串的支持由以下三个类提供

classbasestring(object)
class str(basestring)
class unicode(basestring)

str和unicode都是basestring的子类。严格意义上说,str其实是字节串,它是unicode经过编码后的字节组成的序列。对 UTF-8编码的str'汉'使用len()函数时,结果是3,因为实际上,UTF-8编码的'汉' == '\xE6\xB1\x89'。

unicode才是真正意义上的字符串,对字节串str使用正确的字符编码进行解码后获得,并且len(u'汉') == 1。

#!/usr/bin/envpython
#-*- coding:utf-8 -*-

a= '你好'
b= u'你好'

print(type(a),len(a)) #output : (<type'str'>, 6)

print(type(b),len(b)) #output : (<type'unicode'>, 2)


4.2 Python3

Python3中对字符串的支持进行了实现类层次的上简化,去掉了unicode类,添加了一个bytes类。从表面上来看,可以认为Python3中的str和unicode合二为一了。

classbytes(object)
classstr(object)

实际上,Python3中已经意识到之前的错误,开始明确的区分字符串与字节。因此Python3中的str已经是真正的字符串,而字节是用单独的bytes类来表示。也就是说,Python3默认定义的就是字符串,实现了对UNICODE的内置支持,减轻了程序员对字符串处理的负担。 

#!/usr/bin/envpython
#-*- coding:utf-8 -*-

a= '你好'
b= u'你好'
c= '你好'.encode('gbk')

print(type(a),len(a)) #output : <class'str'> 2
print(type(b),len(b)) #output : <class'str'> 2
print(type(c),len(c)) #output : <class'bytes'> 4

5、字符编码的转换

Unicode字符串可以与任意字符编码的字节串进行相互转换,如图:

 Python字符编码问题学习


从上图可以看出不同字节编码之间是可以通过Unicode来实现相互转换的。

 

Python2中的字符串进行字符编码转换过程是:

字节串(Python2的str默认是字节串)-->decode('原来的字符编码')-->Unicode字符串-->encode('新的字符编码')-->字节串

#!/usr/bin/envpython
#-*- coding:utf-8 -*-


utf_8_a= '我爱中国'
gbk_a= utf_8_a.decode('utf-8').encode('gbk')
print(gbk_a.decode('gbk'))
# 输出结果: 我爱中国

Python3中定义的字符串默认就是unicode,因此不需要先解码,可以直接编码成新的字符编码:

 

字符串(str就是Unicode字符串)-->encode('新的字符编码')-->字节串

 

#!/usr/bin/envpython
#-*- coding:utf-8 -*-


utf_8_a= '我爱中国'
gbk_a= utf_8_a.encode('gbk')
print(gbk_a.decode('gbk'))
输出结果: 我爱中国


 





 参考:

http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=IWS-Chapter04a

http://www.cnblogs.com/yyds/p/6171340.html

http://www.cnblogs.com/JohnABC/p/4015504.html

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html