(1)理解编码和解码
虽然我们接触到的数据是各种字符等,但计算机存储数据是用二进制数据的方式。二进制数据是由0和1两个数字组成的数据,是计算机最基本的存储单位。计算机中的所有数据都以二进制形式进行存储和处理。计算机的存储在物理上是二进制的,任何一个数据都是用一堆0和1来表示。一个0或1表示一个比特(bit)。在计算机中一个二进制称为1比特(bit),八位的二进制称为1字节(byte)。
字符转化为二进制数据的过程:一个字符先根据某种编码方式转化为对应的数字,然后这个数字再转化为二进制数字(为了操作方便,通常转化为16进制传给计算机)。这个转换过程就是编码。
按不同的转换规则就涉及到不同的编码方式。比如字符串"你好,中国",按"gbk"编码方式对应的二进制数据为
bash
b'\xc4\xe3\xba\xc3\xa3\xac\xd6\xd0\xb9\xfa'
其中\x表示16进制数,c4表示大小,占一个字节
解码就是将二进制数据翻译为我们能理解的字符。
(2)乱码怎么来的
如果编码时按A规则,解码时按B规则,就可能出现乱码,所以编码和解码一般需要保持统一。

(3)常见编码
ASCII码(1字节)
ASCII码是American Standard Code for Information Interchange的缩写,美国信息交换标准码。
计算机最早是美国人发明的,英文中只需要使用英文字母、数字、特殊符号以及计算机的控制符。当时在他们看来,计算机中字符只需要100多个就够了,因此他们选择使用7位的二进制来表示字符的编码。用0到127来对应键盘上的128个字符。
十进制中,数字 0 的 ASCII 码是 48,字母 A 的 ASCII 码是 65,字母 a 的 ASCII 码是 97
任何小写字母的 ASCII 码减去其大写字母的 ASCII 码都是 32。
python中 ASCII码值与字符的转化函数
python
if __name__ == "__main__":
# ASCII码值转字符
print(chr(49)) # 对应字符1
print(chr(50)) # 对应字符2
# 字符转ASCII码值
print(ord('A')) # 65
print(ord('x')) # 120
后面由于欧洲比如法语等,128个字符不够用,ASCII扩展到0-255,具体细节就不说了。因此一个ASCII码占用8位,也就是占用1字节内存大小。
ANSI码
在不同的操作系统中,ANSI码对应不同的编码:
- 在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;
- 在英文Windows操作系统中,ANSI 编码代表 ASCII编码;
- 在繁体中文Windows操作系统中,ANSI编码代表Big5;
- 在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。
Unicode编码(4字节)
前面讲了ASCII码的知识,大家看出来,英文在电脑中的编码是没问题了。但是计算机走向全世界时,问题来了,电脑中如何表示其他各国的语言呢?我们暂且不说别的国家的文字,就中文里面使用的汉字就多达几万个,再加上俄语的西里尔字母、阿拉伯语的字母等等。
为了保持全世界语言在计算机中统一的表示,人们制定了Unicode(万国码)的标准,确保了世界上所有的语言都能够使用统一的标准编码。
Unicode最早使用的是2个字节,奈何字符数量实在太多,最后扩展成了4个字节。
Python 3中字符串类型中的字符使用的就是Unicode。
Unicode码称做统一码,以"\u"开始,从"\u0000"到"\uFFFF"
python中查看 Unicode码值对应的字符
python
python中查看Unicode码值对应的字符
if __name__ == "__main__":
print("\u6B22") # 欢
print("\u8FCE") # 迎
print("\u3434") # 㐴
Unicode编码产生前各国的编码
Unicode是1991年开始制定的,在制定Unicode之前,各个国家都制定了各自的字符编码,以保证字符在计算机中的正常显示。如中国大陆的gb2312、香港台湾地区的BIG5、日本的SHIFT-JIS等等。使用不同的编码看字符会出现"乱码"现象。
GBK编码
中国大陆的gb2312和香港台湾地区的BIG5合并以后形成一个中文字符编码的超集,名字叫GBK。目前我们使用的Windows操作系统简体中文版中仍然使用的是GBK编码。
UTF-8编码
Unicode虽然很好,但是却有很大的问题。英文字母使用ASCII码原本只要一个字节就能表示的,使用Unicode却需要4个字节,白白浪费了空间。尤其是在网络传输时,白白浪费了网络带宽。于是UTF-8编码方式就诞生了。
UTF-8是一种针对Unicode的可变长度字符编码,它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容。在UTF-8中,英文字符仍然是1个字节,汉字占几个字节是不确定度,有可能2个字节、3个字节或者4个字节。
(4)python编码和解码接口
encode和decode函数
python
if __name__ == "__main__":
# 1.编码函数 encode
data_str = "你好,中国"
data_bin = data_str.encode("gbk")
print(data_bin) # b'\xc4\xe3\xba\xc3\xa3\xac\xd6\xd0\xb9\xfa'
# 2.解码函数
result = data_bin.decode("gbk")
print(result) # 你好,中国
# 3.如果编码和解码用不同的方式则可能会出错
# result = data_bin.decode("utf-8")
# 比如data_bin是按gbk编码,这里却用utf-8解码,就会报错
# UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc4 in position 0: invalid continuation byte
# 4.解码失败,强行解码
result = data_bin.decode("utf-8", "ignore")
print(result) # ãй
# 可以看到结果不对,所以还是不要强行解码的好,不过有时也需要强行解码
bytes函数
python
if __name__ == "__main__":
# 编码也可以用bytes 函数,不过必须指明编码方式
# 1.对中文字符进行编码
data = "你好,中国"
res1 = bytes(data, "gbk")
res2 = bytes(data, "utf-8")
print(res1) # b'\xc4\xe3\xba\xc3\xa3\xac\xd6\xd0\xb9\xfa'
print(res2) # b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\xad\xe5\x9b\xbd'
print(res1.decode("gbk")) # 你好,中国
print(res2.decode("utf-8")) # 你好,中国
# 如果内容是纯英文字母和数字,可以直接在字符串的引号前加字母b,就可以将变量类型定义为二进制序列类型
data = b'hello world'
print(data.decode("utf-8")) # hello world
print(data.decode("gbk")) # hello world
# 2.对纯英文字母和数字的字符进行编码,等价于直接在字符串的引号前加字母b,占一个字节
x = bytes('0', 'utf-8')
print(x) # b'0'
x = bytes('0', 'gbk')
print(x) # b'0'
y = bytes('A', 'utf-8')
print(y) # b'b'A'
y = bytes('A', 'gbk')
print(y) # b'b'A'
(5)gbk和utf-8编码方式下英文字母和汉字字符占几个字节
先说验证结论:
- 对于英文字母都是占一个字节
- 对于汉字字符:
- utf-8编码方式下一个汉字占3个字节(前面两个字符表示汉字,最后那个字符表示结束)
- gbk编码方式下一个汉字占2个字节
python
if __name__ == "__main__":
# 1.对于英文字母都是占一个字节
data = "y"
print(data.encode("utf-8")) # b'y'
print(data.encode("gbk")) # b'y'
# 2.对于汉字字符
# utf-8编码方式下一个汉字占3个字节(前面两个字符表示汉字,最后那个字符表示结束)
# gbk编码方式下一个汉字占2个字节
data = "年"
print(data.encode("utf-8")) # b'\xe5\xb9\xb4'
print(data.encode("gbk")) # b'\xc4\xea'
data = "过年"
print(data.encode("utf-8")) # b'\xe8\xbf\x87\xe5\xb9\xb4'
print(data.encode("gbk")) # b'\xb9\xfd\xc4\xea'
end