python 编码与解码 II

参考链接:https://www.jianshu.com/p/19c74e76ee0a

                  https://www.runoob.com/python/python-chinese-encoding.html

我们再简单梳理一下:具体看 python 编码与解码

1 字符与字节

一个字符不等价于一个字节,一个字节由8个0/1表示比特位,即数值上最大为128.

字符是人类能够识别的字体符号,而这些符号要保存到计算机的存储中,就需要用计算机能够识别的字节来表示。

一个字符往往有多种表示方法(字符编码),不同的表示方法会使用不同的字节数。比如字母A-Z都可以用ASCII码表示(占一个字节),也可以用Unicode表示(占两个字节),还可以用UTF-8表示(占一个字节)。

 

字符编码的作用就是将人类可识别的字符转换为机器可识别的字节码,解码就是将机器可识别的字节码转换成人类可识别的字符。

 

Unicode才是真正的字符串,而用ASCII、UTF-8、GBK等字符编码表示的是字节串。从上面对各种编码方式的介绍中,我们也可以了解到,像ASCII、UTF-8、GBK这些都是编码方式,是将字符编码为字节码。

Unicode只是一个符号集,它只规定了人类现实生活所有字体符号的二进制代码,也就是说它给每一个字符一个独一无二的数字来表示。(所以可以等价理解Unicode就是人类字符,因为它两是可以一一映射的)


2 编码与解码

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

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

简单理解:编码是给计算机底层用的,也是机器唯一能看懂的。解码是显示给人看的,凡是人眼能识别的字符都是要解码过的。


3 Python中的默认编码

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

我们都知道,磁盘上的文件都是以二进制格式存放的,其中文本文件都是以某种特定编码后的字节形式存放的。

对于程序源代码文件的字符编码是由编辑器指定的。比如我们使用Pycharm来编写Python程序时会指定工程编码和文件编码为UTF-8(#-*-coding:utf-8-*- ),那么Python代码被保存到磁盘时就会被转换为UTF-8编码对应的字节(encode过程)后写入磁盘。

当执行Python代码文件中的代码时,Python解释器在读取Python代码文件中已经被编码的字节串之后,我们会在控制台窗口看看程序到底输出了什么,这时候其实就是将字节码转换为Unicode字符串(decode过程)之后才能被我们看到结果。

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"进行解码,导致中文字符解码失败,出现如下错误:语法 错误,在ASCII里面根本没有这个字节码,那可不,ASCII只能存数字,字母,所以说明白了不可以用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的解释器以"UTF-8"作为默认编码,在Linux上没问题,可以不用看了。但是这并不表示可以完全兼容中文问题。比如我们在Windows上进行开发时,Python工程及代码文件都使用的是默认的GBK编码(因为win系统上默认是GBK,但是如果在python代码开头指定用utf-8却可以用utf-8编解码),也就是说Python代码文件默认是被转换成GBK格式的字节码保存到磁盘中的。Python3的解释器执行该代码文件时,试图用UTF-8进行解码操作时,同样会解码失败,出现如下错误:因为不同编码,有不同的编码规则,结果在一些码表里面可能不存在该字节串,尽管gbk能够包含中文。我们一旦编码和解码用的不一致就会乱码。

但是控制窗口好像默认用系统的指定编解码方式,所以有时候发现写入文件ok,控制台输出却乱码了,或者报错了,那是因为控制台用系统的编解码,可能与我们指定的源代码编解码方式不一样导致的。源代码用utf-8编码后被机器执行,机器输出结果显示的时候用gbk解码,这时候是可能乱码的。

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

正确做法如下:

  1. #!/usr/bin/env python

  2. #-*- coding: utf-8 -*-

第一行注释事为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释,所以在win下面写代码,有时候大家会习惯不写第一行代码;

第二行注释市为了告诉Python解释器,按照UTF-8编码读取源代码,否则,在源代码中写的中文输出可能会有乱码

或者 #coding=utf-8 就行了

注意:#coding=utf-8 的 = 号两边不要空格。

注意:Python3.X 源码文件默认使用utf-8编码,所以可以正常解析中文,Linux上其实无需指定 UTF-8 编码。

4 Python2、Python3对字符串的支持

4.1 Python2

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

class basestring(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/env python
#-*- 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合二为一了。

class bytes(object)
class str(object)

实际上,Python3中已经意识到之前的错误,开始明确区分字符串与字节串。因此Python3中的str已经是真正的字符串,而字节是用单独的bytes类来表示。(这个就符合我们现在实际的定义了)python2里面把Unicode作为中间的桥梁。

即 字符 (一一映射)----Unicode(中间桥梁)------(编解码)-----字节。----- 都是双向可逆的过程。其实python2更细化了整个具体过程,python3更加整体上进行封装。

也就是说,Python3默认定义的就是字符串,实现了对Unicode的内置支持,减轻了程序员对字符串处理的负担。

#!/usr/bin/env python
#-*- 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

4.3 比较

对于单个字符的编码,Python提供了ord()函数获取字符的整数数值表示,chr()函数把编码数值转换为对应的字符:

>>> ord('A')
65
>>> ord('中')
20013
>>> chr(97)
'a'
>>> chr(20013)
'中'

如果知道字符的整数编码,还可以用十六进制这么写str:

>>> '\u4e2d\u6587'
'中文'

两种写法完全是等价的。

 

由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。

Python对bytes类型的数据用带b前缀的单引号或双引号表示:x = b'ABC'。

要注意区分'ABC'和b'ABC',前者是str,后者虽然内容显示得和前者一样,但bytes的每个字符都只占用一个字节。前者共3个字符,如果每个字符用一个字节的话,也要3个字节表示

python3的str通过encode()方法可以编码为指定的bytes,例如:

>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '中文'.encode('ascii')
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in

纯英文的str可以用ASCII编码为bytes,内容是一样的,含有中文的str可以用UTF-8编码为bytes。

含有中文的str无法用ASCII编码,因为中文编码的范围超过了ASCII编码的范围,Python会报错。

在bytes中,无法显示为ASCII字符的字节,用\x##显示。

反过来,如果我们从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就需要用decode()方法

>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'

python3要计算str包含多少个字符,可以用len()函数:可以发现str就是字符串了,而非python 2 的字节串。

>>> len('ABC')
3
>>> len('中文')
2

len()函数计算的是str的字符数,如果换成bytes,len()函数就计算字节数:

>>> len(b'ABC')
3
>>> len(b'\xe4\xb8\xad\xe6\x96\x87')
6
>>> len('中文'.encode('utf-8'))
6

可见,python3中 1个中文字符经过UTF-8编码后  通常    会占用3个字节,而1个英文字符只占用1个字节。因为其他任何的编码方式已经兼容ASCii,毕竟这个领域是西方人先发明的啊。其实有过一些经验就发现,电脑安装国外工具软件一般存放路径不要有中文就是这个道理,因为可能会乱码,他们还是用ASCii编码解码的。编写程序的时候一般就尽量用英文来避免不必要的麻烦。至少目前 的编程语言绝大部分都是英文字母的,O(∩_∩)O哈哈~

所以英文部分和ASCii是一样的。ASCII编码实际上可以被看成是UTF-8编码的一部分。

 

在操作字符串时,我们经常遇到str和bytes的互相转换。为了避免中文乱码问题,应当始终坚持使用UTF-8编码对str和bytes进行转换。而且编解码一定要保持一样的。

当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这两行:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释。

第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

5 字符编码的转换

Unicode字符串可以与任意字符编码的字节串进行相互转换,如图:在这里就可以理解前面说的Unicode其实是中间桥梁的真正的意义了。因为每个字符都与Unicode码表的数值一一对应,所以二者其实不用太刻意的去区分。

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

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

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

#!/usr/bin/env python2
#-*- 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/env python3
#-*- coding:utf-8 -*- 
utf_8_b = '我爱中国'
gbk_b = utf_8_b.encode('gbk')
print(gbk_b.decode('gbk'))
# 输出结果:我爱中国

 



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值