Python编码问题一直困扰了我许久,之前有过一些总结,但并不系统,比较凌乱。当然python2.x编码问题本身,便是剪不断理还乱。本篇将系统介绍python2.x编程中会遇到的一些编码问题,并给出解决方案。基于对编码问题的摸索了解,我也尝试写了一个编码转换模块
Transcode
,应该能解决绝大部分新手的疑难杂症。当然,python大神可以绕道而行,至于使用3.x的朋友,以后将会成文介绍。
python编程中会经常遇到操作系统编码、文件编码、控制台输入输出编码、网页编码、源代码编码、python编码,本文将会逐一介绍。首先我们来看看一些常见的编码情况:
print sys.getdefaultencoding() #系统默认编码
print sys.getfilesystemencoding() #文件系统编码
print locale.getdefaultlocale() #系统当前编码
print sys.stdin.encoding #终端输入编码
print sys.stdout.encoding #终端输出编码
将以上这段代码在windows与linux系统下分别运行,查看输出结果。
windows终端结果:
ascii
mbcs
('zh_CN', 'cp936')
cp936
cp936
Linux终端结果:
ascii
UTF-8
('zh_CN', 'UTF-8')
UTF-8
UTF-8
操作系统默认编码可以通过sys.getdefaultencoding()函数获取,可以看到windows与linux下默认都为ascii编码,而我们知道ascii编码不支持中文。那么操作系统编码将在python程序的何处会被用到呢?何时又会引发血案?
经过测试,我发现当需要将unicode格式的字符串存入到文件时,python内部会默认将其先转换为Str格式的系统编码,然后再执行存入步骤。而在这过程中,容易引发ascii异常。
实例证明:
#! -*- coding:utf-8 -*-
a=u"中文"
f=open("test.txt","w")
f.write(a)
报错异常信息:UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 0-1……
说明:因为ascii不支持中文,而变量a为unicode格式的中文字符串,因此无法进行编码而引发异常。
设置系统编码为utf-8或者gbk。
import sys
reload(sys)
sys.setdefaultencoding('gbk')
说明:在windows下将其设置为gbk,在linux在设置为utf-8.
windows下终端指的是控制台,在控制台上输入输出有着其本身的编码格式,如windows控制台输入输出编码都为cp936。原谅我是第一次看到此编码,于是上网查了会,发现其实它就是常见的GBK编码;而linux终端的输入输出编码都为utf-8。如果我们编写的程序,不会再终端输入输出任何内容,则可以忽略此编码,如若不然终端编码将会非常重要。
我们在终端执行python脚本时,经常会遇到输出中文乱码,而这往往是因为输出的字符串本身编码与控制台编码不一致。
实例证明:
#! -*- coding:utf-8 -*-
a="中文" #定义一个变量,默认为Str,utf-8编码
print a
print type(a)
windows控制台输出结果:
浣犲ソ
<type 'str'>
linux终端输出结果:
中文
<type 'str'>
造成这种差异的原因在于windows控制台为gbk编码,而变量a本身为utf-8编码。
#! -*- coding:utf-8 -*-
a='你好'
b=a.decode("utf-8").encode("gbk")
print b
将变量a从utf-8编码转换为gbk编码。
python2.x从外部获取的内容都是string编码,其内部分为String编码与Unicode编码,而String编码又分为UTF-8,GBK,GB2312等等。因此为了避免不同编码造成的报错,python内部最好都转化为unicode编码,在输出时再转化为str编码 。可以用encode()/decode()函数,将string与unicode编码互换。
基本在于python内部变量编码与控制台编码,或者其他编码相结合时触发。
实例证明:
#! -*- coding:utf-8 -*-
a="中文" #定义一个变量,默认为str,utf-8编码
print a
print type(a)
运行结果:
浣犲ソ
<type 'str'>
说明:windows下控制台输入输出都是gbk编码格式,而代码中定义的变量a为str,utf-8格式,所以会出现乱码。如果想创建一个unicode编码字符串的变量,则可以a=u”123”,在双引号前面加上一个u,表示a为unicode编码。
#! -*- coding:utf-8 -*-
a='你好'
print a.decode("utf-8").encode("gbk")
说明:首先我们定义的变量a是str格式,编码为utf-8的字符串,我们要将之转化为str格式,GBK编码的字符串。在python内部无法直接转化,需要借助decode()与encode()函数。decode()函数先将str格式的字符串a转化为unicode,再将unicode编码为str格式GBK。而在Unix系统下,不存在这个问题,因为都是utf-8编码,不会存在乱码。print语句默认会将unicode编码的字符串,encode为相应系统的str编码并输出(windows下为gbk,unix下为utf-8),因此不用担心print unicode编码字符串会报错。
源代码编码指的是python程序本身的编码,默认为ascii。
python程序本身要被解释器解析执行,需要先被转化为二进制代码。而在这过程中容易引发异常,原因同样是ascii不支持中文,因此当python程序中出现中文时,哪怕是注释,也会引发ascii异常。
实例证明:
print "中文" #中文注释
报错:SyntaxError: Non-ASCII character ‘\xe7’……
#! -*- coding:utf-8 -*-
python程序开头加上这句代码,指定python源代码编码格式为utf-8。
文件编码指的是,python程序从文件中获取的内容的编码格式。可以用sys.getfilesystemencoding()函数获取,windows下为mbcs,linux下为utf-8。至于mbcs,是一种多字节编码(没搞很明白)。
当python程序从文件中获取内容,并输出时,容易触发异常。
实例证明:
#! -*- coding:utf-8 -*-
f=open("test.txt","r")
content=f.read()
print type(content)
print content
运行结果:
<type 'str'>
你好
可以看到windows下,从文件中读取的编码格式为Str,GBK格式(因为控制台输出没有中文乱码);而在Unix下为Str,Utf-8格式。从输出内容来说,并没有触发异常,然而当这些内容与python程序自身内容相结合时,容易触发异常。
在windows下,最好将文件内容转为unicode,可以使用codecs:
f=codecs.open("test.txt", encoding='gbk').read()
将格式为gbk的文件内容转化为unicode格式,当然也可以直接使用open(“”,”r”).read().decode(“gbk”)
参考操作系统编码触发异常点,即将中文unicode字符写入文件时,容易触发异常。
参考操作系统编码解决方案,或者手动将unicode编码转换为str编码。
实例证明:
#! -*- coding:utf-8 -*-
a=u"中文" #a为unicode格式编码
f=open("test.txt","w")
f.write(a.encode("gbk"))
当然如果变量a本身就是Str则不会报错,只是utf-8编码的内容写入windows文件中,显示会乱码。
网页编码,通常在写爬虫的时候经常遇到,再结合系统编码,python编码,文件编码,往往会搞得一团乱。在程序中我们应该分别处理这些编码,在python内部全部转化为unicode。那么网页编码又有哪些格式呢?
常见格式:utf-8,gbk,gb2312
还是在于从网页中获取的源码编码与终端编码,甚至python内部编码不一致的情况。
实例证明:
#!coding=utf-8
import urllib2
body=urllib2.urlopen('http://thief.one').read()
print type(body)
print body
运行结果:
<type 'str'>
body中文显示乱码
说明:这个网站的编码是utf-8,而且python从网页上爬取的内容都为Str格式,在windows控制台下输出会乱码。
依照之前做法,先将其转化为unicode。而相应的正则也可以为unicode编码,如:res=r’’+u”新成员”。可以通过chardet模块判断网页编码类型,返回的是一个带概率的字典。
isinstance(obj, (str, unicode))
返回True或者False
import chardet
import urllib2
body=urllib2.urlopen("http://thief.one").read()
chardet.detect(body)
判断编码格式,会有百分比,一般用来判断网页编码比较好。
print sys.getdefaultencoding() #系统默认编码
print sys.getfilesystemencoding() #文件系统编码
print locale.getdefaultlocale() #系统当前编码
print sys.stdin.encoding #终端输入编码
print sys.stdout.encoding #终端输出编码
实例一:
a="\\u8fdd\\u6cd5\\u8fdd\\u89c4"
print a
变量a的内容本身为unicode编码,怎么正常显示输入?
解决方案:
a="\\u8fdd\\u6cd5\\u8fdd\\u89c4" # unicode转化为中文
b=a.decode('unicode-escape')
print b
如果阅读完本章,增加了您对python编码问题的认识,那我会感到欣慰,如有python编码上的问题可以在下方留言。
如果阅读完本章,您仍然不知如何解决python乱码问题,没关系,请继续移步阅读
Transcode解决python编码问题
为了能够让您重视,我不得不再次重申:解决python2.x编码问题的关键,在于要明白无论从哪里来的内容,在python内部流通时,都应该先转换为unicode。(python3.x在这方面做了改进,并取得了很好的效果)