数据编码(二)彻底弄懂Python文件编码问题

3,638次阅读
一条评论

上一篇我们搞懂了 ASCII 和 Unicode 以及 UTF-8 之前的关系,接下来又遇到 Python 的编码,弄得我是云里雾里的。也是之前遗留下来的问题,一直也没搞明白,今天我读了很多大佬的技术博客,终于是弄懂了。那么现在我来写明白它,并分享给同样困惑的你~,以下解释均是建立在 Python3 的基础上

1、代码编码问题

相信很多小伙伴都听别人说过这样的话,Python3 的默认编码方式是以 UTF-8,听着有点玄幻,啥叫 Python 的默认编码?

当我们编译运行一个py文件(test.py)的时候,Python 编译器首先会对文件 test.py 进行读取,然后默认对数据进行 UTF-8 的解码,然后编译运行,程序跑了起来。

我们知道,数据的解码和编码都是成对的,且需要采取相同的编码方式,否则解码的数据就会和原数据不一样。其实也类似于 AES 的解密和加密。

试想一下,test.py 文件是由一个文本编辑器以 GBK 的编码方式写入,此时采用默认的 UTF-8方式进行解码,就会造成乱码情况。

不信的话,show code with you:

# 测试环境:
OS: Mac os 10.15
IDE: Pycharm
Python: Python3.8
Author: 西园公子

1.1、案例1:

测试脚本 test.py ,脚本文件以 UTF-8 编码的方式保存的。

内容:

# coding=gbk
# Author: zwjjiaozhu
# Date: 2021/1/8
# IDE: VsCode

import sys
print(sys.getdefaultencoding())
name = '小甲'
print(f"name: {name}\n name_type: {type(name)}\n :{repr(name)}")

with open('utf.txt', 'w', encoding='utf8') as f:
    f.write(name)

with open('gbk.txt', 'w', encoding='gbk') as f:
    f.write(name)

结果:

utf-8
name: 灏忕敳
name_type: <class 'str'>
:'灏忕敳'

# 写入文件内容
灏忕敳
小甲

此时你该困惑了,这都是啥呀?为啥打印显示灏忕敳。以 GBK 的方式写入文件,却内容显示正常为小甲,以 UTF-8 的方式却显示为灏忕敳

接下来我来讲明白它:

上面的代码首行添加了 # coding=gbk ,这就告诉编译器需要使用 GBK 的解码方式对 test.py 文件进行解码,转成相应的 Unicode 码,然后就运行代码了。

由于name = '小甲' 是中文,所以当使用GBK进行解码,翻译成 Unicode 码值后,打印显示后就出现了乱码问题(也就是值不对应的问题),其他的代码啥的都是字母,不同的编码解码方式都是可以正常显示的,谁让是美国人发明的呢😂

然后是写入文件:

  • encoding='utf8' ,将编译器解码的 name 的值(灏忕敳)以UTF-8的编码方式写入文件utf.txt中,当使用记事本打开utf.txt文件时,记事本默认是以UTF-8的解码方式打开,就显示 灏忕敳 了。编码和解码成对的,媒介是 UTF-8
  • encoding='gbk' ,同理,将编译器解码name后的Unicode值(灏忕敳对应的Unicode值)以GBK的编码方式写入文件gbk.txt中。如果此时采用UTF-8的解码方式打开这个文本,就会显示小甲正常,如果采用GBK的方式打开,那不好意思,还是显示 灏忕敳 。相当于是python编译器对test.py 的GBK解码和写入文件的GBK编码抵消,得到的就是最开始的 test.py 以UTF-8编码的数据。

可能你还是不太懂,强烈推荐手动画画草图,这里我画一个流程图,以加深理解~

数据编码(二)彻底弄懂Python文件编码问题

/>

数据编码(二)彻底弄懂Python文件编码问题

/>

哇好累呀,肝的我脑疼,总算解释清楚了,

2、字符串编码

在 Python 中有两种常见格式,字符串和字节 bytes类型,在网络传输中以及写入文件时,通常是转成bytes。例如:"你好" b“\xe4\xbd\xa0\xe5\xa5\xbd”(utf-8编码的) ,两者是等价的。

2.1、字符串和字节互转

字节是以 \x开头后跟16进制数

方式1,使用encode和decode方式直接转

name = "你好"
name_bytes = name.encode("utf8")   # 用utf8进行编码,用gbk或者ascii都行
name2 = name_bytes.decode("utf8")   # 同样的必须使用用utf8进行解码,做到编码和解码是对应的,才不会乱码
print(f"name:{name},type:{type(name)}")
print(f"name_bytes:{name_bytes},type:{type(name_bytes)}")
print(f"name2:{name2},type:{type(name2)}")

# 结果:
# name:你好,type:<class 'str'>
# name_bytes:b'\xe4\xbd\xa0\xe5\xa5\xbd',type:<class 'bytes'>
# name2:你好,type:<class 'str'>

方式2,使用 str 和 bytes 转

age = '12'
age_bytes = bytes(age, encoding="utf8")
age_str = str(age_bytes.decode("utf8"))
print(f"age:{age},type:{type(age)}")
print(f"age_bytes:{age_bytes},type:{type(age_bytes)}")
print(f"age_str:{age_str},type:{type(age_str)}")

# 结果:
# age:12,type:<class 'str'>
# age_bytes:b'12',type:<class 'bytes'>
# age_str:12,type:<class 'str'>

说明:字符串在Python内部也就是内存中是以 Unicode 方式存在,因此,在做编码转换时,通常需要以 Unicode 作为中间编码,即先将其他编码的字符串解码(decode)成 Unicode ,再将unicode编码(encode)成另一种编码。

decode 的作用是将 bytes 字节解码成 str 类型字符串,如str1.decode('gb2312'),表示将字符串 str1 以gb2312 的解码方式解码转换成 str 类型,也就是 Python 的str类型 。

encode 的作用是将 str 类型字符串 编码转换成 bytes 字节,如str2.encode('gb2312'),表示字符串 str2 以gb2312 的编码方式转换成 bytes 类型。因此,转码的时候一定要先搞明白,数据 str1 是什么编码,用什么方式编码的,就用什么方式解码。

总结:在 Python 中一般就是字节和字符串直接的转换,尤其是在爬虫中:有很多关于编码的问题,从获取到的网页源码中有很多是采用 UTF-8 进行编码,这时在解码数据的时候应采用 decode('utf-8')进行解码,获得正确的显示样式,但是有些网页是采用 GBK 进行编码的(采用 GBK 解码),就会有很多的问题,获得数据都是以乱码的!

总结:总算是写完了,反复思考了好几遍,对文件的编码理解更加透彻。下期会写 Python 对 json 文件的各种操作以及字典和 json 的区别,另外还有 Base64 的编码算法~

参考文章:

https://zhuanlan.zhihu.com/p/40834093 https://www.liaoxuefeng.com/wiki/1016959663602400/1017075323632896

7
西园公子
版权声明:本站原创文章,由西园公子2021-01-09发表,共计3306字。
转载提示:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(一条评论)
载入中...
西园公子 博主
2021-01-10 09:23:55 回复

有写的不对的地方,欢迎各位大佬指正,谢谢~