Python编码问题

Python编码问题总结

下面的文章和编码具体解释总结了常见字符编码后,这篇文章会对python中常见的编码问题进行分析和总结。因为python3.x版本号和python2.x版本号在字符编码方面有非常大差异,所以本文都是以Python2.7.5来分析2.x版本号中的字符编码问题。


1.Python编码基础

1.1 str和unicode

python中有两种数据模型来支持字符串这样的数据类型,str和unicode,它们的基类都是basestring。

比方s = "中文"就是str类型的字符串,而u=u"中文"就是一个unicode类型的字符串。unicode是由str类型的字符串解码后得到。unicode也能够编码成str类型。即

str --> decode -->unicode
unicode --> encode --> str

严格来说,str或许应该叫做字节串,由于对于UTF-8编码的str类型"中文",使用len()函数得到的结果是6,由于UTF-8编码的str类型“中文”实际是"xe4xb8xadxe6x96x87"

而对于unicode类型u“中文”(实际是u"u4e2du6587"),使用len()函数得到结果是2.

1.2 头部编码声明

在python源码文件里假设实用到非ascii字符,比方中文,那么须要在源码文件头部声明源码字符编码,格式例如以下:

#-*- coding: utf-8 -*-

这个格式看起比較复杂,事实上python仅仅检查#、coding,编码等字符串,能够简写成#coding:utf-8,甚至还能够写成#coding:u8。


2.Python2.x常见编码问题

2.1 头部编码声明和文件编码问题

文件头部编码声明决定了python解析源代码中的str的编码选择方式,比方头部声明的是utf-8编码,则代码中s="中文"python就会依照utf-8编码格式来解析,通过repr(s)能够看到字符编码是"xe4xb8xadxe6x96x87"。假设头部声明的编码是gbk编码,则python会对s採用gbk编码解析。结果是"xd6xd0xcexc4"

须要注意的是,文件本身的编码要跟文件头部声明编码一致,不然就会出现故障。

文件本身的编码在Linux以下能够在vim下用命令set fenc来查看。

假设文件本身编码是gbk。而源代码文件头部声明的编码是utf-8,这样假设源代码中有中文就会有问题了,由于本身中文str存储是依照gbk编码来的。而python在解析str的时候又以为是utf-8编码。这样就会报SyntaxError: (unicode error) 'utf8' codec can't decode byte错误。

2.2 默认编码问题

以下看个python默认编码导致的问题:

#coding: utf-8
u = u"中文"
print repr(u) # u'u4e2du6587'

s = "中文"
print repr(s) # 'xe4xb8xadxe6x96x87'

u2 = s.decode("utf-8")
print repr(u2) # u'u4e2du6587'

#s2 = u.decode("utf-8") #编码错误
#u2 = s.encode("utf-8") #解码错误

注意实例中凝视掉的2行代码,对于unicode最好不要直接调用decode。str最好不要直接调用encode方法。由于假设是直接调用。则相当于u.encode(default_encoding).decode("utf-8")。default_encoding是python的unicode实现中用的默认编码,即sys.getdefaultencoding()得到的编码,假设你没有设置过。那么默认编码就是ascii,假设你的unicode本身超出了ascii编码范围就会报错。同理,假设对str直接调用encode方法,那么默认会先对str进行解码。即s.decode(default_encoding).encode("utf-8"),假设str本身是中文,而default_encoding是ascii的话。解码就会出错,从而导致上面这两行会分别报UnicodeEncodeError: 'ascii' codec can't encode characters in position...错误和UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position...错误。

上面样例中凝视掉的两行代码假设运行就会报错,当然,假设本身str或者unicode都在ascii编码范围。就没有问题。比方s = "abc"; s.encode("utf-8")就不会有问题。语句运行后会返回一个跟s的id不同的str。

那假设要解决实例1中的问题,有两种方法,其一是明白指定编码,例如以下所看到的:

#coding: utf-8
u = u"中文"
print repr(u) # u'u4e2du6587'

s = "中文"
print repr(s) # 'xe4xb8xadxe6x96x87'

u2 = s.decode("utf-8")
print repr(u2) # u'u4e2du6587'

s2 = u.encode("utf-8").decode("utf-8")  # OK                                    
u2 = s.decode("utf8").encode("utf-8")   # OK

另外一种方法就是更改python的默认编码为文件编码格式,例如以下所看到的(这里仅仅所以要reload sys模块。是由于python初始化后删除了setdefaultencoding方法):
#coding:utf-8                                                                   

import sys 
reload(sys)
sys.setdefaultencoding("utf-8") #更改默认编码为utf-8

u = u"中文"
print repr(u) # u'u4e2du6587'

s = "中文"
print repr(s) # 'xe4xb8xadxe6x96x87'

u2 = s.decode("utf-8")
print repr(u2) # u'u4e2du6587'

s2 = u.decode("utf-8")
u2 = s.encode("utf-8")

2.3读写文件编码

採用python的open()方法打开文件时,read()读取的是str,编码就是文件本身的编码。而调用write()写文件时,假设參数是unicode,则须要用指定编码encode,假设write()參数是unicode并且没有指定编码,则会採用python默认编码encode后再写入。

#coding:utf-8                                                                   
f = open("testfile")
s = f.read()
f.close()
print type(s) # <type 'str'>

u = s.decode("utf-8") #testfile是utf-8编码
f = open("testfile", "w")
f.write(u.encode("gbk")) #以gbk编码写入,testfile为gbk编码
f.close()

此外,python的codecs模块提供了一个open()方法。能够指定编码打开文件。使用这种方法打开文件读取返回是unicode。

写入时,假设write參数是unicode,则使用打开文件时的编码写入,假设是str,则先使用默认编码解码成unicode后再以打开文件的编码写入(这里须要注意假设str是中文,而默认编码sys.getdefaultencoding()是ascii的话会报解码错误)。

#coding:gbk
import codecs

f = codecs.open('testfile', encoding='utf-8')
u = f.read()
f.close()
print type(u) # <type 'unicode'>

f = codecs.open('testfile', 'a', encoding='utf-8')
f.write(u) #写入unicode

# 写入gbk编码的str,自己主动进行解码编码操作
s = '汉'
print repr(s) # 'xbaxba'
# 这里会先将GBK编码的str解码为unicode再编码为UTF-8写入
#f.write(s) #默认编码为ascii时。这会报解码错误。
f.close()


3.參考资料

版权声明:本文博客原创文章,博客,未经同意,不得转载。

原文地址:https://www.cnblogs.com/mengfanrong/p/4637458.html