最近在编写阿里云MaxCompute的过程中遇到了字符编码的问题,由于之前一直使用的Python3,就没有太多的字符编码问题,而刚好MaxCompute使用的Python2.7版本的基础库,导致出现一直编码错误的问题,问题虽然最后都解决了,但是解决的过程却是艰辛的,顾写一遍系统性的总结文章,如果你遇到了跟我一样的问题,便可以通过这边文章学习到解决此类的问题根本,一劳永逸。
什么是字符编码
首先需要知道什么是字符编码,为什么会出现这个东东。我们都知道计算机只能处理0,1数字的数据,而我们使用的文本数据就必须先转换成二进制格式的数据才能被计算机识别。而数据存储的最小单位是比特(bit),8个比特等于一个字节,一个字节能表示的最大比特数字是11111111,即十进制数字256-1=255。若想存储更大的数字就需要使用更多的字节。比如双字节存储的最大数字是65536-1=65535,4个字节能表示最大数字是2的4x8=32次方,即4294967296-1=4294967295。
美国人当时发明计算机的时候为了方便,只是把常用的英文字母和数字,符号用单个字节来表示,比如用二进制1000001表示大写的字母A,用二进制1100001表示小写的字母a,这个编码规则就是ASCII编码,在Python中使用ord函数可以打印出对应的数字表示。
import sys
print(format(ord('A'),'b')) #二进制表示
print(ord('A')) # 十进制表示
print(format(ord('a'),'b')) #二进制表示
print(ord('a')) # 十进制表示
#查看单个字符的二进制,十进制,十六进制表示
print(format(ord('鹏'),'b')) #二进制表示
print(ord('鹏')) # 十进制表示
print(hex(ord('鹏')))# 十六进制进制表示
然而中文的字符库远远大于英文字符库,所以需要单独一个编码规则,这个编码规则还不能和ASCII编码规则冲突,我们把它命名为GB2312。
最后就是每个国家都需要发布自己的编码规则,如果我一篇文章中即想要显示日文有想要显示中文汉字。这个时候就会显示乱码。因为找不到对应的编码规则。为了解决这个问题,人们提出了unicode编码规则,这种规则采用2个字节代表一个字符,特殊字符采用4个字节。但是这种存储的方式随着网络的发展,不能适应大规模存储及传输的场景,一篇全是英文的文章因为使用了unicode编码技术会导致双倍占用空间和带宽的浪费。这个时候人们又提出了utf-8编码技术,这是一种可变长编码技术,对于大多数字符采用单个字节编码,不够的情况下采用4个字节编码。所有utf-8一般用来存储文件,unicode一般用来在内存存储中,不做永久性存储。历史总是在不断的重演,都是遵循发现问题--》提出问题--》解决问题的思路在演进。
python默认采用什么编码规则
如何知道默认编码规则
import sys
print(sys.getdefaultencoding())
如果你使用的Python2,那么得到的结果就是:ascii,如果是python3则得到默认编码规则:utf-8。这时我们试着同时输出两句中文字符“大鹏码字“”看看结果会怎么样
结果是Python2会提示Non-ASCII character,这句话的意思就是没有找到“大鹏码字”四个汉字的ASCII编码规则,ASCII采用单个字节,最多只能保留255个字符。显然是不够的。相反python3默认采用utf-8文件编码技术,UTF-8采用可变长编码规则就没有问题。
如何改变Python2的默认编码规则
在文件的头部添加如下代码可以改变文件的默认编码
# coding=utf-8 将系统默认ASCII编码更改为utf-8
import sys
print '大鹏码字'
打印出来任然是乱码,这是因为CMD的默认编码是GB2312,UTF-8直接以GB2312的格式输出是有问题,在CMD控制台下输入CHCP命令可以查看到活动页是936,代表的就是GB2312编码
要想输出正确的中文,需要进行unicode的编码转换。这个只是和系统环境有关系。
# coding=utf-8 将系统默认ASCII编码更改为utf-8
import sys
import chardet #检测字符编码
print chardet.detect('大鹏码字')
# 检测到当前的字符编码是 utf-8
# {'confidence': 0.938125, 'language': '', 'encoding': 'utf-8'}
print '大鹏码字' #输出乱码 utf-8--GB2312 转码失败
print '大鹏码字'.decode('utf-8') #输出正确 utf-8--unicode--GB2312转码成功
回到问题的最后解决办法encode,decode
出现问题的地方一个是文件编码,内存编码,系统环境变量。为了解决这种问题,我们需要用到编码,解码技术。
从其他编码变成unicode我们称之为解码decode,反之则为编码encode。所有的编码问题基本都是因为没有经过unicode这个中间编码而导致的,特别是在Python2的环境下,Python3则一次性设置了所有的默认规则,包括文件编码utf-8以及内存编码unicode,内部会自动进行解码,编码操作。省去了自己设置这些编码的问题。
最后总结一下
- python2环境下需要设置文件头手动改变文件编码,python3不需要默认就是utf-8。
- python2环境下需要手动进行utf-8到unicode的解码才能在CMD模式输出正确结果,python3不需要。
- 获取文件的默认编码规则使用sys.getdefaultencoding()
- 使用chardet组件可以检测字符的编码规则
以上只是我个人的验证工作,如果有问题可以关注我讨论各种问题,所有代码代码已放在GitHub上面
https://github.com/dapengmazi/python-encode-study.git