转载自:https://blog.csdn.net/ggggiqnypgjg/article/details/53271541
背景
多次被python的编码/乱码问题困扰,相信pythoner们都被困扰过,网上铺天盖地的资料太多也参差不齐,就整理了下。本文从使用的角度系统总结了python编码相关的一些概念,将本文的例子玩一遍,基本上对python的编码问题就清楚了。
首先明确几个概念:
- 字节流:以utf8/gbk等编码编码的字节流。
- unicode对象:python代码中,
a=u'中国'
, 或者a='中国'.decode()
的结果。 - terminal用于显示字符的编码:将一个用utf8/gbk编码的字节流通过terminal指定的编码,去查找对应的字符显示出来。
- locale:linux下,Locale 是软件在运行时的语言环境, 它包括语言(Language), 地域 (Territory) 和字符集(Codeset)。一个locale的书写格式为: 语言[_地域[.字符集]]. 所以说呢,locale总是和一定的字符集相联系的。比如:zh_CN.GB2312
- 编码转换原则:unicode是”中介”,任何编码之间转换都需要先decode()到unicode。
针对python,先把结论放在前面,三点:
- #coding:utf-8 #.py文件是什么编码就需要告诉python用什么编码去读取这个.py文件。
- sys.stdout.encoding,默认就是locale的编码,print会用sys.stdout.encoding去encode()成字节流,交给terminal显示。所以locale需要与terminal一致,才能正确print打印出中文。
- 对编码字符串a,代码中可以直接写a.encode(“gbk”),但事实上内部自动先通过defaultencoding 去decode成unicode之后再encode()的。
- str(xxx)应该也是用这个去编码的。
'ascii' codec can't encode characters in position 7-8: ordinal not in range(128)
print的时候出现这个错误一般可以使用这个方案去处理。- 为了避免代码中到处都要去encode(“xxx”),还有可能不同的地方写得不一样带来不一致的情况,推荐使用这个:
import sys reload(sys) sys.setdefaultencoding('utf8')
- 1
- 2
- 3
例子1:
- 在python中,unicode vs 字节流:字节流可以从unicode encode得到,unicode可以从utf8/gbk等编码的字节流decode得到。
- 分析下面这段代码,终端/locale分别为不同编码的情况:
#coding:utf-8 #由于.py文件是utf-8的,所以必须有这一句 import sys import locale import os import codecs reload(sys) print sys.getdefaultencoding() + " - sys.getdefaultencoding()" sys.setdefaultencoding('utf8') #影响encode() print sys.getdefaultencoding() + " - sys.getdefaultencoding()" print sys.stdout.encoding + " - sys.stdout.encoding:" #sys.stdout = codecs.getwriter('utf8')(sys.stdout) #影响print print sys.stdout.encoding + " - sys.stdout.encoding:" u = u'中国' print u + " - u" a = '中国' print a + " - a" print a.decode('utf-8') + " - a.decode('utf-8')" print a.decode('utf-8').encode('gbk') + " - a.decode('utf-8').encode('gbk')" print a.decode('utf-8').encode('utf-8') + " - a.decode('utf-8').encode('utf-8')" print a.decode('utf-8').encode() + " - a.decode('utf-8').encode()" print (sys.stdout.encoding) + " - (sys.stdout.encoding)" print (sys.stdout.isatty()) print (locale.getpreferredencoding()) print (sys.getfilesystemencoding())
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
ascii - sys.getdefaultencoding() utf8 - sys.getdefaultencoding() GBK - sys.stdout.encoding: GBK - sys.stdout.encoding: 中国 - u 涓???? - a 中国 - a.decode('utf-8') 中国 - a.decode('utf-8').encode('gbk') 涓???? - a.decode('utf-8').encode('utf-8') 涓???? - a.decode('utf-8').encode() GBK - (sys.stdout.encoding) True GBK utf-8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
ascii - sys.getdefaultencoding() utf8 - sys.getdefaultencoding() UTF-8 - sys.stdout.encoding: UTF-8 - sys.stdout.encoding: 涓???? - u 涓???? - a 涓???? - a.decode('utf-8') 中国 - a.decode('utf-8').encode('gbk') 涓???? - a.decode('utf-8').encode('utf-8') 涓???? - a.decode('utf-8').encode() UTF-8 - (sys.stdout.encoding) True UTF-8 utf-8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
例子1总结,对print而言:
- unicode的数据如果要显示正常,必须终端与locale一致。sys.stdout.encoding这个值应该来自locale,print会以sys.stdout.encoding去encode并输出到字节流。
最终是terminal通过terminal配置的编码规则去解码成对应的字符并显示出来。
例子2:
关于sys.setdefaultencoding(‘utf8’)的例子:
#coding:utf-8 import sys reload(sys) sys.setdefaultencoding('utf8') print sys.getdefaultencoding() + " - sys.getdefaultencoding()" a = '中国' print a + " - a" print a.encode("gbk") #并不是直接从utf8的字节流转化到gbk的,而是通过defaultencoding decode之后才转的。 print a.decode() #使用默认的defaultencoding print a.encode() #使用默认的defaultencoding
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
关于str()和repr()
- str()是对各种类型转化成str,如果本来是encoded字符串,则不变,如果为unicode,会encode()
测试环境locale为GBK
#coding:utf-8 import sys reload(sys) sys.setdefaultencoding("utf-8") a = u'中国' print a print str(a) print repr(a) print repr(a.encode("utf-8")) print repr(a.encode("gbk"))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
中国 涓???? u'\u4e2d\u56fd' '\xe4\xb8\xad\xe5\x9b\xbd' '\xd6\xd0\xb9\xfa'
- 1
- 2
- 3
- 4
- 5
再深挖下去,还有repr()和eval()的关系,就不深挖了。
关于终端和服务器的编码
4. echo “中国年过” > a.txt #这个情况下,只有terminal与locale的编码一致,你才能在终端shell打出正确的中文~~~所以a.txt与两者都会一致
参考资料
关于vim:http://blog.chinaunix.net/uid-21843387-id-106001.html
文章来源: python中,中文乱码问题的解决