一、编解码简介

1、编码

在显示器上看见的文字、图片等信息在电脑里面其实并不是我们看见的样子,即使你知道所有信息都存储在硬盘里,把它拆开也看不见里面有任何东西,只有些盘片。假设,你用显微镜把盘片放大,会看见盘片表面凹凸不平,凸起的地方被磁化,凹的地方是没有被磁化;凸起的地方代表数字1,凹的地方代表数字0。硬盘只能用0和1来表示所有文字、图片等信息。那么字母”A”在硬盘上是如何存储的呢?可能小张计算机存储字母”A”是1100001,而小王存储字母”A”是11000010,这样双方交换信息时就会误解。比如小张把1100001发送给小王,小王并不认为1100001是字母”A”,可能认为这是字母”X”,于是小王在用记事本访问存储在硬盘上的1100001时,在屏幕上显示的就是字母”X”。也就是说,小张和小王使用了不同的编码表。小张用的编码表是ASCII,ASCII编码表把26个字母都一一的对应到2进制1和0上;小王用的编码表可能是EBCDIC,只不过EBCDIC编码与ASCII编码中的字母和01的对应关系不同。一般地说,开放的操作系统(LINUX 、WINDOWS等)采用ASCII 编码,而大型主机系统(MVS 、OS/390等)采用EBCDIC 编码。在发送数据给对方前,需要事先告知对方自己所使用的编码,或者通过转码,使不同编码方案的两个系统可沟通自如。

总而言之,有了编码,便可以将机器语言逐渐翻译成自然语言。

2、unicode编码

如上ANSI编码条例中所述,世界上存在着多种编码方式,在ANSi编码下,同一个编码值,在不同的编码体系里代表着不同的字。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码,可能最终显示的是中文,也可能显示的是日文。在ANSI编码体系下,要想打开一个文本文件,不但要知道它的编码方式,还要安装有对应编码表,否则就可能无法读取或出现乱码。为什么电子邮件和网页都经常会出现乱码,就是因为信息的提供者可能是日文的ANSI编码体系和信息的读取者可能是中文的编码体系,他们对同一个二进制编码值进行显示,采用了不同的编码,导致乱码。这个问题促使了unicode码的诞生。

总而言之,unicode码用于解决数字与二进制的映射,进而方便翻译成自然语言。

3、ASCII、GB2312、GBK编码

每个unicode数字用固定宽度的二进制位表示,比如都用两字节,由此产生了ASCII、GB2312、GBK编码。

4、UTF-8编码

存储的二进制位除了表示数字之外,还表示每个unicode数字的长度,由此产生了utf-8编码。

二、实战:字节与字符串相互转换

import binascii
import struct

data_zh = "网络超时"
data_en = "timeout"

# 一、字符串转字节
# 1.1、中文字符串转字节(不同编码格式展示):直接选择编码方式进行编码,就能转字节
gb_zh = data_zh.encode("GB2312")
print(gb_zh)  # b'\xcd\xf8\xc2\xe7\xb3\xac\xca\xb1'
gbk_zh = data_zh.encode("GBK")
print(gbk_zh)  # b'\xcd\xf8\xc2\xe7\xb3\xac\xca\xb1'  备注:GB2312是GBK的子集
utf_zh = data_zh.encode("utf-8")
print(utf_zh)  # b'\xe7\xbd\x91\xe7\xbb\x9c\xe8\xb6\x85\xe6\x97\xb6'
# print(data_zh.encode("ASCII"))  # 不支持

# 1.2、英文字符串转字节(不同编码格式展示):直接选择编码方式进行编码,就能转字节
gb_en=data_en.encode("GB2312")
print(gb_en)  # b'timeout'
gbk_en=data_en.encode("GBK")
print(gbk_en)  # b'timeout'
utf_en = data_en.encode("utf-8")
print(utf_en)  # b'timeout'
asc_en=data_en.encode("ASCII")
print(asc_en)  # b'timeout'

# 1.3、字符串转16进制字节:先将字符串转字节,再转16进制
gb_zh_16 = binascii.b2a_hex(gb_zh)
print(gb_zh_16)  # b'cdf8c2e7b3accab1'
gb_en_16=binascii.b2a_hex(gb_en)
print(gb_en_16)  # b'74696d656f7574'

# 1.4、任意字符为16进制的字符串转字节:直接编码即可
gb_zh_16_str='cdf8c2e7b3accab1'
print(bytes().fromhex(gb_zh_16_str))  # b'\xcd\xf8\xc2\xe7\xb3\xac\xca\xb1'

# 1.5、任意字符为16进制的字符串转16进制字节:直接编码即可
gb_zh_16_str='cdf8c2e7b3accab1'
print(gb_zh_16_str.encode())  # b'cdf8c2e7b3accab1'

# 二、数字转字节
# 2.1、数字转普通字节、大端序或小端序字节:直接通过struct模块打包实现
num = 123
print(struct.pack('@L', num))  # b'{\x00\x00\x00'  按原字节转化成的普通字节
print(struct.pack('=L', num))  # b'{\x00\x00\x00'  按标准方式转化成的普通字节
print(struct.pack('<L', num))  # b'{\x00\x00\x00' 小端序字节,相关参数请参考文章:
print(struct.pack('>L', num))  # b'\x00\x00\x00{' 大端序字节

# 2.2、数字转16进制字节:先将数字转化成字节,再将字节格式化成16进制
num_byte = struct.pack('@L', num)
print(binascii.b2a_hex(num_byte))  # b'7b000000' 字符串类型  注意,原编码决定了16进制长度和排序

# 三、数字转字符串
# 3.1、数字转普通字符串
print(str(num))  # 123 字符串
# 3.2、数字转16进制字符串
print(hex(num))  # 0x7b

# 四、字节转字符串
# 4.1、任意字节均为16进制字符串,转字符串:直接转即可
print(binascii.b2a_hex(num_byte).decode())  # 7b000000  字符串
# 4.2、普通字节转16进制字符串:先将字节进行16进制处理,再转字符串
# print(num_byte.decode())  # 非16进制字符串,直接解不出来
num_byte_16=binascii.b2a_hex(num_byte)
print(num_byte_16.decode())  # 7b000000  字符串

# 五、字节转数字
# 4.1、普通字节转数字:需要知道原先的编码方式
print(struct.unpack('@L', num_byte))  # (123,)
# 4.2、任意字节都为16进制的字节转数字:先转化成字符串,再数字
print(int(num_byte_16.decode(), 16))  # 2063597568