Python的解码与编码

一、字符与编码的关系

在学习之前,我们学习几个概念,让我们达成一个共识。理论上,从一个字符到具体的编码,会经过以下几个概念。
字符集(Abstract character repertoire)
编码字符集(Coded character set)
字符编码方式(Character encoding form)
字符编码方案(Character encoding scheme )

字符集:也就是我们肉眼能识别的文字,如中文、英语、德语。

编码字符集:是一个从整数集子集到字符集抽象元素的映射,即给抽象的字符编上数字
如 Unicode 中的定义的字符,每个字符都有个数字和它对应,一只对应着一个字符。
反过来,则不一定是。这里所说的映射关系,是数学意义上的映射关系。
编码字符集也是与计算机无关的,ASCII、UTF、Unicode、GBK等字符集也在这一层。
如汉字“中文”的Unicode的编码是:\u4e2d\u6587;utf-8编码为 b'\xe4\xb8\xad\xe5\x9b\xbd',GBK的编码为:b'\xd6\xd0\xb9\xfa'

字符编码方式:这个开始与计算机有关了,编码字符集的编码点在计算机里的具体表现形式
通俗的说,意思就是怎么样才能将字符所对应的整数的放进计算机内存、或文件、或网络中。
于是,不同人有不同的实现方式,所谓的万码奔腾,就是指这个;GB2312、UTF-8、UTF-16、UTF-32等都在这一层。
如汉字“中文”的Unicode编码为“\u4e2d\u6587”,他在计算机的存储按十六进制展开为:‭‭100 1110 0010 1101 0110 0101 1000 0111‬;

编码方案:这个更加与计算机密切相关,具体是与操作系统密切相关,主要是解决大小字节序的问题。
对于UTF-16和UTF-32编码,Unicode都支持big-endian 和 little-endian两种编码方案。


一般来说,我们所说的编码、解码,都在第二、三层完成,而序列化、反序列化在第一层。

二、编码与解码

为了简化理解,在Python3当中,只有两种编码,Unicode与 bytes,而Unicode=str,他们的转换关系:

str-->str.encode('字符编码') -->bytes -->bytes.decode('字符编码') --> str

编码(encode)就是将字符串(str)转换成字节码(bytes);
解码(decode)就是将字节码(bytes)转换为字符串(str);

举个栗子:

oath = '中文'
utf8 = oath.encode('utf-8')
oath1 = utf8.decode('utf-8')
print("对‘中文’按utf-8进行编码:",utf8,',长度:',len(utf8),'类型:',type(utf8),'占用字节:',sys.getsizeof(utf8))
print('对编码过的变量进行解码:',oath1,'长度:',len(oath1),'类型:',type(oath1),'占用字节:',sys.getsizeof(oath1))

运行结果:

对‘中文’按utf-8进行编码: b'\xe4\xb8\xad\xe6\x96\x87' ,长度: 6 类型: <class 'bytes'> 占用字节: 39
对编码过的变量进行解码: 中文 长度: 2 类型: <class 'str'> 占用字节: 78

证明str==Unicode

oath = '我爱妞'
print(oath,',长度:',len(oath),'类型:',type(oath),'占用字节:',sys.getsizeof(oath))
oath1 = u'我爱妞'
print(oath1,',长度:',len(oath1),'类型:',type(oath1),'占用字节:',sys.getsizeof(oath1))
print('oath==oath1:',oath==oath1,'所以,str实际存储的是Unicode字符,那么也可以Unicode编码来存储str')
我爱妞 ,长度: 3 类型: <class 'str'> 占用字节: 80
我爱妞 ,长度: 3 类型: <class 'str'> 占用字节: 80
oath==oath1: True 所以,str实际存储的是Unicode字符,那么也可以Unicode编码来存储str

另:

oath2='\u5220\u9664'
print(r'所以说这样形式的Unicode字符:\u5220\u9664,可以直接显示出正确的编码:',oath2,type(oath2))
print('也可以编码为二进制字符:',oath2.encode(),type(oath2.encode()),'所以,字符串常量,前缀带不带u,都是一样的')
print(r'再解码b\xe5\x88\xa0\xe9\x99\xa4,为:',oath2.encode().decode('utf-8'),type(oath2.encode().decode('utf-8')))
所以说这样形式的Unicode字符:\u5220\u9664,可以直接显示出正确的编码: 删除 <class 'str'>
也可以编码为二进制字符: b'\xe5\x88\xa0\xe9\x99\xa4' <class 'bytes'> 所以,字符串常量,前缀带不带u,都是一样的
再解码b\xe5\x88\xa0\xe9\x99\xa4,为: 删除 <class 'str'>

接下来我们来看一个例子(运行环境:Python3):

b'\xc0\xeb\xc0\xeb\xd4\xad\xc9\xcf\xb2\xdd\xa3\xac\xd2\xbb\xcb\xea\xd2\xbb\xbf\xdd\xc8\xd9'

上面这一串二进制字符是我们从某个文件读取到的字符串,我们需要知道它是什么意思,怎么办呢?

这就需要我们对他进行解码(decode)了,在解码之前我们需要知道他是什么类型的编码,才能用对应的方式进行解码,当然,你也可以一个一个的去试,但这不太程序员。

import chardet

data = b'\xc0\xeb\xc0\xeb\xd4\xad\xc9\xcf\xb2\xdd\xa3\xac\xd2\xbb\xcb\xea\xd2\xbb\xbf\xdd\xc8\xd9'
print(type('data'),'检测这串bytes字符是什么编码:',chardet.detect(data))
#结果:<class 'str'> 这串bytes字符是什么编码: {'encoding': 'GB2312', 'confidence': 0.7407407407407407, 'language': 'Chinese'}
print('然后可以根据检测出来的编码进行解码:',data.decode('GB2312'))
print('这也就解释了用什么方式编码,就要用什么方式解码')
#print('当然你也可以试试用utf-8解码,你就会发现他报错:',data.decode('utf-8'))

运行结果:

<class 'str'> 检测这串bytes字符是什么编码: {'encoding': 'GB2312', 'confidence': 0.7407407407407407, 'language': 'Chinese'}
然后可以根据检测出来的编码进行解码: 离离原上草,一岁一枯荣
这也就解释了用什么方式编码,就要用什么方式解码

参考原文:

https://blog.csdn.net/qq_38607035/article/details/82591931

python编码转换 以及转换成unicode报错的问题
https://www.cnblogs.com/reyinever/p/7931811.html
 
 python 查看当前字符串的编码格式
https://blog.csdn.net/sinat_24648637/article/details/84190482

https://www.zhihu.com/question/31833164

Python2 与 Python3 的编码对比
http://kuanghy.github.io/2016/10/15/encoding-python2-vs-python3

python3 三种字符串(无前缀,前缀u,前缀b)与encode()
https://blog.csdn.net/anlian523/article/details/80504699

廖雪峰的官网 字符串和编码
https://www.liaoxuefeng.com/wiki/1016959663602400/1017075323632896
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章