中国程序员始终绕不过的的坑大概就是编码问题了,无数次的中文乱码总是令人头疼!!!
虽然说了很多遍但还还是重复一遍解释中文乱码的原因吧
最先出场的就是ASCII码了,美帝人民发明计算机后,把英文26个字母和常用的符号加起来也就百来个字符,用1字节8位(0-255)一一映射也就绰绰有余了,
之后随着计算机的发展,各国需要显示本土的文字也一一发明了自己的编码表。
然后出场的便是ISO了,本着先来后到的原则,欧洲人接触到计算机后,在ASCII中把德语法语等非英语的字符也塞进了空余的为之中,美国人编码才用了7个字节,再塞下德语法语之后终于把第八个字节也用上了
随后就是GBK了,计算机传到中国后,面对中文的博大精深,一个字节实在难以满足需求,于是gbk使用了两个字节来表示中文,同时也为了兼容ASCII,在第一位设置为标志位,如果为0 只一字节,ascii 剩下7个字节就可以表示了,为1则是读两个字节映射中文。
所以在任何时候英文都不会乱码的原因就是之后的编码表都是在兼容ASCII码上设计的。
但是这样各国之间的数据便不互通了,最终国际组织站出来了,使用了6个字节将全世界的语言都映射成一张编码表,这就是unicode码了,但在讲究效率的年代明明一个字节表示的字符(如ascii码),前面非要再传5个 00000000 的字节实在是浪费,所以之后人们有规定了边长码传输unicode码的协议,这边有了utf-7,utf-8,utf-16等等协议
而我们现在最常用的便是utf-8了,他的编码规则是:如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的字节数,其余各字节均以10开头。UTF-8转换表表示如下:
Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1个字节时:第一位为0后7位表示数据
大于1个字节时:第一个字节在0之前有几个1就表示这个字符用了几个字节表示,后面的字节都以10开头
所有有效表示unicode编码的数据都是在一个字节读取0后开始计算的,所以2个字节utf-8编码能表示11位的Unicode编码....
unicode只是一张映射表,把世界所有的字符都映射了,而utf-8只是unicode编码传输时的一种实现
再来说说python中编码的问题
在python2时代 字符串和字节没有很好的区分,在python3中将两种完全分开为字符串str和字节bytes,str即是unicode编码,而byte则是真正的二进制,只是用了16进制形式表现: \xe4,
但你可能会问 Unicode 只是一张编码表,人为定义了二进制的每个值等于对应的字符啊,其根本他也是一串二进制。对的,但这就是python的机制,对于字符串str在从内存读到显示器上时,python自动完成了解码工作让你看到的不是一串二进制,而是这串二进制根据unicode对应的字符,但对于字节bytes来说,python不会对他处理,就这么原汁原味的呈现给你,所以你看到的str是字符串而bytes则是16进制表示的二进制。
那为什么有时候python里的字符串会是乱码呢,那是因为python自动对unicode解码的规则是utf-8,如果原本字符串的二进制不是unicode码那么解码过后自然就是乱码了
encode和decode
对于字符串str只有一个编码方法 .encode()
默认是以utf-8编码,生成的是对应的字节bytes。也即是告诉python,str不需要自动编码了,就以二进制显示。
对于字节bytes只有一个解码方法.decode()
默认也是utf-8解码,生成对应的字符串str。也就是相当于告诉python,将我解码后再显示出来,就不再是二进制的形式了。
当然编解码都可以以指定的格式编解码,类似 .encode('gbk') 。
unicode的编解码
有时我们还会遇到以\u开头的字节 ,例如 b'\u4e2d' , 这表示的是这是一个原生Unicode字节(注意unicode和utf-8的字节是不一致的 ,前文已经说过了),所以以默认的utf-8编码去对这种unicode是不能正确解码的,我们需要使用特定的unicode_escape解码格式才能正确解码。
b'\u4e2d'.decode('unicode_escape')
同理你也可以以unicode_escape编码,将一个字符串转化为原生的unicode字节
总结
如果你还是有些迷糊,那你可以记住 str是python自动帮你解码后给你呈现的字符串 ,bytes是直接给你看到二进制字节,无论str还是bytes在python里本质都是unicode编码。当然这也是我个人的理解,可能会存在偏差,欢迎各位互相交流