最近被python2的编码弄得心烦意乱,决心花点时间搞懂这一块,省的以后遇到编码解码报错或是乱码再去查资料。以下是做的一些总结。
python编码顺序
网上很多资料都在说python2编码应该如何如何,不过应该先分析一下哪里用到了编解码以及顺序是怎样的。
首先,我们用编辑器写程序,源码文件在保存时会按照某种编码格式存储,而python解释器载入程序会按照默认的编码格式(python2是ASCII ,python3是utf-8)或是头部指定的编码方式去解码。
其次,解释器也有默认编码,当程序中一些行为没有指定编码方式的时候就会用默认编码。
最后,执行prnit
语句时,终端会用终端的编码格式去解码,然后打印出来。
头部声明编码格式
最常见的错误就是python2默认编码是ascii码,程序中出现中文时就会报错。注意,只要出现中文就会报错,哪怕是注释中有中文,不是变量赋值或是调用encode()
decode()
才报错。因为python解释器加载程序时,试图使用ascii码解码一些中文字符编码后的字节,无法解码产生错误。
通常需要在头部以如下方式声明编码方式:
#coding=utf-8
或是
# -*- coding:utf-8 -*-
那么到底要声明什么编码方式呢?我们可以做个test,在test.py中输入以下两行:
# coding=utf-8
ustr=u'中文'
然后将test.py保存为ascii格式,用python去运行test.py,哦豁,报错。
SyntaxError: (unicode error) ‘utf8’ codec can’t decode byte 0xd6 in position 0: invalid continuation byte
因为文本虽然是用ascii码保存的,但通常编辑器会对中文字符做特殊处理,比如,edit plus会用gbk存储中文字符,而解释器加载时用的utf-8解码,自然报错了。再试试将第一行改为 # coding=gbk
,嗯, 不报错了。
所以关键是,代码文件的保存格式要与文件头部声明的编码格式一致。
python解释器的默认编码
解释清楚这一部分必须要先明确python的unicode对象和str对象,前者可以看成货真价实的字符串对象,而后者虽然披着’str’的外衣但实际上是字节串。
unicode对象转换为str对象使用encode()方法,相当于把字符串编码为字节流。相反的,str对象转换为unicode对象要用decode()方法,相当于把字节流解码为字符串。
那么对一个str对象使用encode()方法会怎么样呢?可以试试。注意,本文之后的测试源文件都保存为utf-8。
# coding=utf-8
str_obj="中文"
str_obj.encode('gbk')
哦豁,又报错了。
‘ascii’ codec can’t decode byte 0xe4 in position 0: ordinal not in range(128)
咦?哪来的ascii码?哦,这就是python默认编码用到的地方了。实际上对一个str对象encode,解释器会先用默认编码将str对象decode成一个unicode对象,然后再encode,过程相当于如下所示:
str_obj.decode('ascii').encode('gbk')
可以修改默认编码方式如下, 但是真的不推荐修改,因为出错的根源并不在这里,而是混淆了encode() decode() 的用法。
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
终端编码
在windows命令行执行 python test.py
,代码如下,猜猜看会发生什么?
# coding=utf-8
str_obj="中文"
uni_obj=u'中文'
print str_obj, repr(str_obj), str_obj.decode('utf-8') #涓枃 '\xe4\xb8\xad\xe6\x96\x87' 中文
print uni_obj, repr(uni_obj), uni_obj.encode('utf-8') #中文 u'\u4e2d\u6587' 涓枃
其中 repr()
是一个将值转换为字符串的函数,且转换为供解释器读取的形式。这里用它来让我们知道解释器看到的到底是什么。
str_ob是一个str对象,是一个字节串。对str使用print方法时,终端会用终端的编码格式进行解码(windows 命令行默认编码方式是gbk),然后打印出来。所以第一行乱码很容易解释了,因为str_obj是utf-8编码的字节,用gbk解码会产生乱码。
而print 一个unicode对象时,会调用系统方法,编码方式为终端的编码格式,因此不会乱码。
在windows命令行中可以用 chcp
指令查看和修改编码格式,默认是 chcp 936
,即gbk编码。
注意事项总结总结
- 程序中出现字符串加前缀u 即程序内部使用unicode对象
- 从外部读如字节流的时候,一定要将这些字节流转化为unicode,在后面的代码中去处理unicode,而不是str
- 代码文件的保存格式要与文件头部声明的编码格式一致
- 不到必须时不要在你的程序里编解码Unicode字符,只在你要写入文件或者数据库或者网络时,才调用encode()函数和decode()函数
以上都是本人的粗浅理解, 有些地方没有深入考究,也不知道理解得对不对。欢迎各位看客指正。