【python测试开发栈】带你完全搞明白python3编码原理

在以前的文章中,咱们介绍过编码格式的发展史:[文章传送门-todo]。今天咱们经过几个例子,来完全搞清楚python3中的编码格式原理,这样你以后写python脚本时碰到编码问题,才能有章可循。python

咱们先搞清楚几个概念:linux

  • 系统默认编码:指python解释器默认的编码格式,在python文件头部没有声明其余编码格式时,python3默认的编码格式是utf-8。
  • 本地默认编码:操做系统默认的编码,常见的Windows的默认编码是gbk,Linux的默认编码是UTF-8。
  • python文件头部声明编码格式:修改的是文件的默认编码格式,只是会影响python解释器读取python文件时的编码格式,并不会改变系统默认编码和本地默认编码。

经过python自带的库,能够查看系统默认编码和本地默认编码shell

Python 3.7.4 (tags/v3.7.4:e09359112e, Jul  8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getdefaultencoding()
'utf-8'
>>> import locale
>>> locale.getdefaultlocale()
('zh_CN', 'cp936')
>>>

注意,由于我在windows系统的电脑上 进行测试,因此系统默认编码返回“cp936”, 这是代码页(是字符编码集的别名),而936对应的就是gbk。若是你在linux或者mac上执行上面的代码,应该会返回utf-8编码。windows

其实总结来看,容易出现乱码的场景,基本都与读写程序有关,好比:读取/写入某个文件,或者从网络流中读取数据等,由于这个过程当中涉及到了编码解码的过程,只要编码和解码的编码格式对应不上,就容易出现乱码。下面咱们举两个具体的例子,来验证下python的编码原理,帮助你理解这个过程。注意:下面的例子都是在pycharm中写的。bash

01默认的编码格式

咱们新建一个encode_demo.py的文件,其文件默认的编码格式是UTF-8(能够从pycharm右下角看到编码格式),代码以下:网络

"""
    @author: asus
    @time: 2019/11/21
    @function: 验证编码格式
"""
import sys, locale


def write_str_default_encode():
    s = "我是一个str"
    print(s)
    print(type(s))
    print(sys.getdefaultencoding())
    print(locale.getdefaultlocale())

    with open("utf_file", "w", encoding="utf-8") as f:
        f.write(s)
    with open("gbk_file", "w", encoding="gbk") as f:
        f.write(s)
    with open("jis_file", "w", encoding="shift-jis") as f:
        f.write(s)


if __name__ == '__main__':
    write_str_default_encode()

咱们先来猜想下结果,由于咱们没有声明编码格式,因此python解释器默认用UTF-8去解码文件,由于文件默认编码格式就是UTF-8,因此字符串s能够正常打印。同时以UTF-8编码格式写文件不会出现乱码,而以gbk和shift-jis(日文编码)写文件会出现乱码(这里说明一点,我是用pycharm直接打开生成的文件查看的,编辑器默认编码是UTF-8,若是在windows上用记事本打开则其默认编码跟随系统是GBK,gbk_file和utf_file均不会出现乱码,只有jis_file是乱码),咱们运行看下结果:编辑器

# 运行结果
我是一个str
<class 'str'>
utf-8
('zh_CN', 'cp936')

# 写文件utf_file、gbk_file、jis_file文件内容分别是:
我是一个str
����һ��str
�䐥�꘢str

和咱们猜想的结果一致,下面咱们作个改变,在文件头部声明个编码格式,再来看看效果。测试

02 python头文件声明编码格式

由于上面文件encode_demo.py的格式是UTF-8,那么咱们就将其变为gbk编码。一样的咱们先来推测下结果,在pycharm中,在python文件头部声明编码为gbk后(头部加上 # coding=gbk ),文件的编码格式变成gbk,同时python解释器会用gbk去解码encode_demo.py文件,因此运行结果应该和用UTF-8编码时同样。运行结果以下:编码

# 运行结果
我是一个str
<class 'str'>
utf-8
('zh_CN', 'cp936')

# 写文件utf_file、gbk_file、jis_file文件内容分别是:
我是一个str
����һ��str
�䐥�꘢str

结果确实是同样的,证实咱们推论是正确的。接下来咱们再作个尝试,假如咱们将(# coding=gbk)去掉(须要注意,在pycharm中将 # coding=gbk去掉,并不会改变文件的编码格式,也就是说encode_demo.py仍是gbk编码),咱们再运行一次看结果:spa

File "D:/codespace/python/pythonObject/pythonSample/basic/encodeDemo/encode_demo.py", line 4
SyntaxError: Non-UTF-8 code starting with '\xd1' in file D:/codespace/python/pythonObject/pythonSample/basic/encodeDemo/encode_demo.py on line 5, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

运行直接报错了,咱们加个断点,看看具体的异常信息:

编码错误.png

看错误提示是UnicodeDecodeError,python解释器在对encode_demo.py文件解码时,使用默认的UTF-8编码,可是文件自己是gbk编码,因此当碰到有中文没办法识别时,就抛出DecodeError。

03 敲黑板,划重点

python3中的str和bytes

python3的重要特性之一就是对字符串和二进制流作了严格的区分,咱们声明的字符串都是str类型,不过Str和bytes是能够相互转换的:

def str_transfor_bytes():
    s = '我是一个测试Str'
    print(type(s))
    # str 转bytes
    b = s.encode()
    print(b)
    print(type(b))
    # bytes转str
    c = b.decode('utf-8')
    print(c)
    print(type(c))


if __name__ == '__main__':
    str_transfor_bytes()

须要注意一点:在调用encode()和decode()方法时,若是不传参数,则会使用python解释器默认的编码格式UTF-8(若是不在python头文件声明编码格式)。可是若是传参的话,encode和decode使用的编码格式要能对应上。

python3默认编码是UTF-8?仍是Unicode?

常常在不少文章里看到,python3的默认编码格式是Unicode,可是我在本文中却一直在说python3的默认编码格式是UTF-8,那么哪一种说法是正确的呢?其实两种说法都对,主要得搞清楚Unicode和UTF-8的区别(以前文章有提到):

  • Unicode是一个字符集,说白了就是把各类编码的映射关系全都整合起来,不过它是不可变长的,所有都以两个字节或四个字节来表示,占用的内存空间比较大。
  • UTF-8是Unicode的一种实现方式,主要对 Unicode 码的数据进行转换,方便存储和网络传输 。它是可变长编码,好比对于英文字母,它使用一个字节就能够表示。

在python3内存中使用的字符串全都是Unicode码,当python解释器解析python文件时,默认使用UTF-8编码。

open()方法默认使用本地编码

在上面的例子中,咱们往磁盘写入文件时,都指定了编码格式。若是不指定编码格式,那么默认将使用操做系统本地默认的编码格式,好比:Linux默认是UTF-8,windows默认是GBK。其实这也好理解,由于和磁盘交互,确定要考虑操做系统的编码格式。这有区别于encode()和decode()使用的是python解释器的默认编码格式,千万别搞混淆了。

总结

不知道你看完上面的例子后,是否已经完全理解了python3的编码原理。不过全部的编码问题,都逃不过“编码”和“解码”两个过程,当你碰到编码问题时,先肯定源文件使用的编码,再肯定目标文件须要的编码格式,只要能匹配,通常就能够解决编码的问题。

相关文章
相关标签/搜索