原文地址:http://www.cnblogs.com/Xjng/p/5093905.htmlhtml
开发常常会遇到各类字符串编码的问题,例如报错SyntaxError: Non-ASCII character
'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
,又例如显示乱码。
因为以前不知道编码的原理,遇到这些状况,就只能不断的用各类编码decode和encode。。。。。
今天整理一个python中的各类编码问题的缘由和解决方法,之后遇到编码问题,就不会像莽头苍蝇同样,处处乱撞了。python
下面的python环境都是在2.7,据说在3.X中已经没有编码的问题了,由于全部的字符串都是unicode了,以后装个3.X试一下。redis
若是不知道什么是decode和encode,建议先看一下:这里数据库
1.在python文件中,若是有中文,就必定要在文件的第一行标记使用的编码类型,例如 #encoding=utf-8
,就是使用utf-8的编码,这个编码有什么做用呢?会改变什么呢?
demo1.pybash
# encoding=utf-8 test='测试test' print type(test) print repr(test)
输出:ide
<type 'str'> '\xe6\xb5\x8b\xe8\xaf\x95test'
咱们经过print把一个变量输出到终端的时候,IDE或者系统通常都会帮咱们的输出做转换,例如中文字符会转成中文,因此就看不到变量的原始内容。
repr函数能够看这个变量的给python看的形式,也就是看到这个变量的原始内容
从上面的输出能够看到test变量的str类型,它的编码是utf-8的(怎么知道是utf-8,请看第三部分),也就是的encoding类型
若是咱们把encoding改成gbk
demo2.py函数
# encoding=gbk test='测试test' print type(test) print repr(test)
输出测试
<type 'str'> '\xb2\xe2\xca\xd4test'
这样test的编码类型就变为gbk了。
因此这个encoding会决定在这个py文件中定义的字符串变量的编码方式。
而若是一个变量是从其余py文件导入,或者从数据库,redis等读取出来的话,它的编码又是怎样的?
a.pyui
# encoding=utf-8 test='测试test'
b.py编码
# encoding=gbk from a import test print repr(test)
输出
'\xe6\xb5\x8b\xe8\xaf\x95test'
a.py中定义test变量,a.py的编码方式是utf-8,b.py的编码方式是gbk,b从a中导入test,结果显示test依然为utf-8编码,也就是a.py的编码
因此encoding只会决定本py文件的编码方式,不会影响导入的或者从其余地方读取的变量的编码方式
codec can't encode characters
的缘由python的程序常常会报错 codec can't encode characters
或 codec can't decode characters
在python中定义一个字符串,
import sys print sys.getdefaultencoding() # 输出 ascii unicode_test=u'测试test' print repr(str(unicode_test))
上面的代码会报错
'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
除了str方法外,若是操做两个都有中文的字符串,也会报错,可是只有其中一个有中文,却不会报错
unicode_test = u'测试test%s{0}' print '%stest' % unicode_test # 不会报错 print '%s测试' % unicode_test #会报错 print unicode_test % 'test' #不会报错 print unicode_test % '测试' #会报错 print unicode_test.format('test') #不会报错 print unicode_test.format('测试') #会报错 print unicode_test.split('test') #不会报错 print unicode_test.split('测试') #报错 print unicode_test + 'test' #不会报错 print unicode_test + '测试' #会报错
为何会这样?
这缘由下面再解答,这里先列出这个报错的解决方法:
解决方法是:把系统的默认编码设置为utf-8
import sys reload(sys) sys.setdefaultencoding('utf-8') print sys.getdefaultencoding() unicode_test=u'测试test'
demo3.py
# encoding=utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
unicode_test=u'测试test'
utf8_test='测试test'
gbk_test=unicode_test.encode('gbk')
#合并unicode和utf-8 merge=unicode_test+utf8_test print type(merge) print repr(merge) #合并unicode和gbk merge=unicode_test+gbk_test print type(merge) print repr(merge) print merge #合并utf-8和gbk merge=utf8_test+gbk_test print type(merge) print repr(merge) print merge
这里定义三个分别是unicode,utf-8和gbk编码的字符串,unicode_test,utf8_test和gbk_test
1.合并unicode和utf-8的时候,输出:
<type 'unicode'> u'\u6d4b\u8bd5test\u6d4b\u8bd5test'
合并的结果的编码是unicode编码。
2.合并unicode和gbk,会报错:
'utf8' codec can't decode byte 0xb2 in position 0: invalid start byte
因此咱们能够推测:
在python对两个字符串进行操做的时候,若是这两个字符串有一个是unicode编码,有一个是非unicode编码,python会将非unicode编码的字符串decode成unicode编码,再进行字符串操做
例如合并字符串的操做能够写成如下的function:
def merge_str(str1, str2): if isinstance(str1, unicode) and not isinstance(str2, unicode): str2 = str2.decode(sys.getdefaultencoding()) elif not isinstance(str1, unicode) and isinstance(str2, unicode): str1 = str1.decode(sys.getdefaultencoding()) return str1 + str2
PS:sys.getdefaultencoding()的初始值是ascii
因此,codec can't encode(decode) characters
这个报错是encode或decode这两个方法产生的,而这个方法的参数是sys.getdefaultencoding()。若是用ascii编码对带有中文的字符串进行解码,就会报错。因此修改系统的默认编码能够避免这个报错。
当执行 str
操做时,python会执行 unicode_test.encode(sys.getdefaultencoding())
,因此也会报错。
3.#合并utf-8和gbk的时候却不会报错,python会直接把两个字符串合并,不会有decode或encode的操做,可是输出的时候,部分字符串会乱码。
demo4.py
# encoding=gbk import sys reload(sys) sys.setdefaultencoding('utf-8') unicode_test = u'测试test' utf8_test = unicode_test.encode('utf-8') gbk_test = unicode_test.encode('gbk') merge = utf8_test + gbk_test print type(merge) print repr(merge) print merge
这里文件的encoding是gbk,sys.getdefaultencoding()设置为utf-8,结果是:
<type 'str'> '\xe6\xb5\x8b\xe8\xaf\x95test\xb2\xe2\xca\xd4test' 测试test����test
即gbk的部分乱码了。因此输出的时候会按照sys.getdefaultencoding()的编码来解码。
而咱们常常遇到的编码其实主要的就只有三种:utf-8,gbk,unicode
\u
带头的,而后后面跟四位数字或字符串,例如 \u6d4b\u8bd5
,一个 \u
对应一个汉字\x
带头的,后面跟两位字母或数字,例如 \xe6\xb5\x8b\xe8\xaf\x95\xe5\x95\x8a
,三个 \x
表明一个汉字\x
带头的,后面跟两位字母或数字,例如 \xb2\xe2\xca\xd4\xb0\xa1
,两个个 \x
表明一个汉字使用chardet模块来判断
```
import chardet
raw = u'我是一只小小鸟'
print chardet.detect(raw.encode('utf-8'))
print chardet.detect(raw.encode('gbk'))
```
输出:
{'confidence': 0.99, 'encoding': 'utf-8'} {'confidence': 0.99, 'encoding': 'GB2312'}
chardet模块能够计算这个字符串是某个编码的几率,基本对于99%的应用场景,这个模块都够用了。
在str中,\x
是保留字符,表示后面的两位字符表示一个字符单元(暂且这么叫,不知道对不对),例如'\xe6'
,通常三个字符单元表示一个中文字符
因此在定义变量时,a='\xe6\x88\x91'
,是表明定义了一个中文字符“我”,可是有时候,咱们不但愿a这个变量表明中文字符,而是表明3*4=12个英文字符,可使用encode('string_escape')
来转换:
'\xe6\x88\x91'.encode('string_escape')='\\xe6\\x88\\x91'
decode就是反过来。
转换先后的类型都是string。
还有一个现象,定义a='\x'
,a='\x0'
都是会报错ValueError: invalid \x escape
的,而定义a='\a'
,即反斜杠后面不是跟x,都会没问题,而定义a='\x00'
,即x后面跟两个字符,也是没问题的。
同理在unicode中,\u
是保留字符,表示后面的四个字符表示一个中文字符,例如b=u'\u6211'
,表示“我:”,同理咱们但愿b变量,表示6个英文字符,而不是一个中文字符,就可使用encode('unicode-escape')来转换:
u'\u6211'.encode('unicode-escape')='\u6211'
注意encode前是unicode,转换后是string。
在unicode中,\u是保留字符,可是在string中,就不是了,因此只有一个反斜杠,而不是两个。
decode就是反过来。
同理,a='\u'
也是会报错的
#正常的str和unicode字符 str_char='我' uni_char=u'我' print repr(str_char) # '\xe6\x88\x91' print repr(uni_char) # u'\u6211' # decode('unicode-escape') s1='\u6211' s2=s1.decode('unicode-escape') print repr(s1) # '\\u6211' print repr(s2) # u'\u6211' # encode('unicode-escape') s1=u'\u6211' s2=s1.encode('unicode-escape') print repr(s1) # u'\u6211' print repr(s2) # '\\u6211' # decode("string_escape") s1='\\xe6\\x88\\x91' s2=s1.decode('string_escape') print repr(s1) # '\\xe6\\x88\\x91' print repr(s2) # '\xe6\x88\x91' # encode("string_escape") s1='\xe6\x88\x91' s2=s1.encode('string_escape') print repr(s1) # '\xe6\x88\x91' print repr(s2) # '\\xe6\\x88\\x91'
内容是unicode,可是type是str,就可使用decode("unicode_escape")
转换为内容和type都是unicode
s1='\u6211' s2=s1.decode('unicode-escape')
内容是str,可是type是unicode,就可使用encode("unicode_escape").decode("string_escape")
转换为内容和type都是str
s1=u'\xe6\x88\x91' s2=s1.encode('unicode_escape').decode("string_escape")
博文为做者原创,未经容许,禁止转载。