说明
这里我们以python2.7为例讲解python的编码方式
指定执行编码方式
python2.7的默认编码方式为ascii字符集,这里所说的编码方式指执行编码方式,在编程过程中,有三个地方都涉及到编码方式:分别是源码编码方式(.py文件的字符集),执行编码方式,运行环境编码方式
所以在源文件的开头,我们往往需要加上
# -*- coding:utf-8 -*-
或者
# -*- coding:gbk -*-
等等来指定一个确定的执行编码方式(内存中加载的字符串使用的就是执行编码方式,C语言中编译链接的可执行文件中字符串的编码方式也是执行编码方式),若不指定,python2.7默认使用ascii(无论源码编码方式是什么,python解释器都将使用ascii读取源文件,且如果碰到ascii之外的字符,则报错),但是python3.6在不指定的情况下,执行编码方式默认使用utf-8编码,所以在这种情况下,python3.6无需显示指定utf-8为执行编码方式。(这一点和c语言的vs的cl.exe等编译器相似,在不指定执行编码方式时,cl.exe默认使用gbk编码,gcc则默认使用与源码编码方式一致的编码,但python和c语言编译器还有不同,c语言允许源码编码方式和执行编码方式不一致,并且编译器会自动将源码中字符串转码成为执行编码方式相应的字符串,即exe文件中的字符串常量编码与源文件中的字符串编码不同,但python如果源码编码与执行编码不一致,将出现乱码,甚至直接报错
重点:
无论是python2.7还是python3.6,执行编码方式和源码编码方式必须一致,虽然文件开头 # -*-coding:xxx -*-
用于指定执行编码方式,但是也会决定python解释器读取源文件时的编码方式,如果源文件是gbk格式,但是开头xxx为utf-8,那么python解释器将会以utf-8编码读取gbk格式的文件,严重时将会导致错误,所以,一定要保证xxx和源文件的编码方式一致,即执行编码方式和源码编码方式一致。
理论上,如果站在C语言中cl.exe编译器的角度来套用python,则无论.py文件的编码方式是什么(只要python.exe解释器能够识别),都与运行结果无关,运行结果只与执行编码方式有关,但上述已说明python中必须保证源码编码方式和执行编码方式一致。另外运行结果的展现方式和环境编码方式有关,例如如果终端的编码方式为utf-8,但是python却输出gbk的字符流( 很容易实现,例如:print("你好".decode("utf-8").encode("gbk")) (py27语法)print("你好".encode("gbk"))(py36语法)),则终端会显示乱码,只需改变终端的编码方式为gbk,即可正常显示,这里终端的编码方式就是环境编码方式。另外,假如终端的编码方式为utf-8,如果接收到unicode的字符编码流,也可以正常识别,但是终端为gbk就不行。
编码与解码
python2.7中有两个函数encode()和decode(),是字符串类型的成员函数,encode()功能是编码,decode()功能是解码,那么用什么字符流来进行编码,又解码成什么字符流呢,答案是unicode,decode()函数功能就是将任何形式的编码字符串,解码成unicode字符集,encode()函数的功能是将unicode字符流编码成指定的字符集,这两个函数都是围绕unicode展开的。
python中任意两种不同字符编码的转换都是以unicode为基石的,在python2.7即使我们想要将utf-8的字符串直接转换成gbk编码的字符串,也会在encode()函数调用前先隐式的以默认方式调用decode(),但由于python2.7的默认执行编码方式为ascii,decode()调用时默认传递的参数也规定为ascii,所以将导致解码失败(以ascii字符集来解码utf-8导致的失败),下一步编码也就无法进行。要想成功,必须通过显示调用str.decode("utf-8").encode("gbk"),显示指定解码方式;在python3中不支持直接将一种编码的bytes类型转换成另一种编码的bytes类型
# -*- coding:utf-8 -*-
import sys
print sys.getdefaultencoding() #打印默认执行编码方式
str = "你好"
#以下一句出错是因为在encode前python会自动调用decode()先将
#字符串解码为unicode,然后在进行编码,由于默认调用decode()
#python2.7中decode()函数的默认参数是ascii,所以会出错,我们
#应该显示调用decode("utf-8")后再调用encode("gbk"),如下第二行
print str.encode("gbk")
print str.decode("utf-8").encode("gbk")
python2.7和python3.6中bytes、str和unicode
a. python2中的str和python3中的bytes本质相同,都是一组二进制字节序列,主要用于存储,当然在python2中也可以用于显示,python3不能用于显示,print一个bytes类型,将会输出它每个字节的具体数值。Python3 中,在字符引号前加‘b’,明确表示这是一个 bytes 类型的对象,实际上它就是一组二进制字节序列组成的数据,bytes 类型可以是 ASCII范围内的字符和其它十六进制形式的字符数据,但不能用中文等非ASCII字符表示;
b. Python2中unicode和python3中的str本质一样,是一组unicode字节流,一般都只用于显示,python2中如果想直接声明unicode类型,直接在字符串前面加u;
c. python2中定义一个str类型,例如str = "hello",它将采用开头指定的编码方式(默认ascii)来存储str;
d. python3中定义一个str类型,无论开头指定的编码方式是什么(不指定,默认utf-8),str类型都是unicode字符编码来存储,我们可以通过str.encode("xxx"),来生成xxx编码类型的bytes类型(所以,个人认为:python3中开头指定编码方式的意义主要是告诉python解释器应该以何种字符编码读取.py文件,若不指定,则默认utf-8,而不是像python2那样还决定str类型的内部编码方式)
python2 与 python3 字节与字符的对应关系
Python2 | Python3 | 表现 | 转换 | 作用 |
str | bytes | 字节 | encode | 存储 |
unicode | str | 字符 | decode | 显示 |