详解python中文编码与处理

转载 2013年12月05日 17:27:19

注意:本文只是针对 python 2,在 python 3 中,编码方式与处理技巧有些许变化,具体请参考:

Python 2 与 Python 3 的差异对比: http://my.oschina.net/leejun2005/blog/173553  

一、使用中文字符

在python源码中如果使用了中文字符,运行时会有错误,解决的办法是在源码的开头部分加入字符编码的声明,下面是一个例子: 
#!/usr/bin/env python 
# -*- coding: cp936 -*- 
Python Tutorial中指出,python的源文件可以编码ASCII以外的字符集,最好的做法是在#!行后面用一个特殊的注释行来定义字符集: 
# -*- coding: encoding -*- 
根据这个声明,Python会尝试将文件中的字符编码转为encoding编码,并且,它尽可能的将指定地编码直接写成Unicode文本。 
注意,coding:encoding只是告诉Python文件使用了encoding格式的编码,但是编辑器可能会以自己的方式存储.py文件,因此最后文件保存的时候还需要编码中选指定的ecoding才行。 
二、中文字符的存储
>>> str = u"中文" 
>>> str 
u'\xd6\xd0\xce\xc4' 
>>> str = "中文" 
>>> str 
'\xd6\xd0\xce\xc4' 
u"中文"只是声明unicode,实际的编码并没有变。这样子就发生变化了: 
>>> str = "中文" 
>>> str 
'\xd6\xd0\xce\xc4' 
>>> str = str.decode("gb2312") 
>>> str 
u'\u4e2d\u6587' 
更进一步: 
>>> s = '中文' 
>>> s.decode('gb2312') 
u'\u4e2d\u6587' 
>>> len(s) 
4 
>>> len(s.decode('gb2312')) 
2 
>>> s = u'中文' 
>>> len(s) 
4 
>>> s = '中文test' 
>>> len(s) 
8 
>>> len(s.decode('gb2312')) 
6 
>>> s = '中文test,' 
>>> len(s) 
10 
>>> len(s.decode('gb2312')) 
7 
可以看出,对于实际Non-ASCII编码存储的字符串,python可以正确的识别出其中的中文字符以及中文上下文中的标点符号。 
前缀“u”表示“后面这个字符串“是一个Unicode字符串”,这仅仅是一个声明,并不表示这个字符串就真的是Unicode了;就好比某正太声称自己已满18岁,但实际上他的真实年龄并不确定,现在体育界年龄造假可不稀罕幺! 
那么声明成u有什么作用呢?对于Python来说,只要你声明某字符串是Unicode,它就会用Unicode的一套机制对它进行处理。比方说,做字符串操作的时候会动用到内部的Unicode处理函数,保存的时候以Unicode字符(双字节)进行保存。等等。显而易见,对于一个实际上并不是Unicode的字符串,做Unicode动作的处理,是有可能会出问题的。 u前缀只适用于你的字符串常量真的是Unicode的情况  
三、中文字符的IO操作
用python处理字符串很容易,但是在处理中文的时候需要注意一些问题。比如: 
a = "我们是python爱好者" 
print a[0] 
只能输出“我”字的前半部分,要想输出整个的“我”字还需要: 
b = a[0:2] 
print b 
才行,很不方便,并且当一段文本中同时有中英文如何处理?最好的办法就是转换为unicode。像这样: 
c = unicode(a, "gb2312") 
print c[0] 
这个时候c的下标对应的就是每一个字符,不再是字节,并且通过len(c)就可以获得字符数!还可以很方便的转换为其他编码,比如转换为utf-8: 
d = c.encode("utf-8") 
四、<type ‘str’>和<type ‘unicode’> 
<type ‘str’>将字符串看作是字节的序列,而<type ‘unicode’>则将其看作是字符的序列,单个字符可能占用多个字节;字节相对于字符,其在存储层次中更低一些。 
str转换为unicode要decode,可以这样想,因为要把字节序列解释成字符序列,字节序列是底层的存放方式,解码(decode)成更高层的字符以便使用;同理,unicode转换为str要encode,就象信息编码(encode)后才存储一样: 
s.decode(encoding) <type 'str'> to <type 'unicode'> 
u.encode(encoding) <type 'unicode'> to <type 'str'> 
例如: 
>>> s = 'str' 
>>> type(s) 
<type 'str'> 
>>> type(s.decode()) 
<type 'unicode'> 
>>> s = u'str' 
>>> type(s) 
<type 'unicode'> 
>>> type(s.encode()) 
<type 'str'> 
处理中文数据时最好采用如下方式: 
1. Decode early(尽早decode, 将文件中的内容转化成unicode再进行下一步处理) 
2. Unicode everywhere (程序内部处理都用unicode) 
3. Encode late (最后encode回所需的encoding, 例如把最终结果写进结果文件) 
下面是一个简单的演示,用re库查询一个中文字符串并打印: 
>>> p = re.compile(unicode("测试(.*)", "gb2312")) 
>>> s = unicode("测试一二三", "gb2312") 
>>> for i in p.findall(s): 
print i.encode("gb2312") 
一二三 
五、跨平台处理技巧
如果一个project必须在两个平台上开发,程序应该使用同样的encoding,比如要求所有的文件都使用UTF-8,如果实在不能统一(一般是为了满足许多所谓专家学者莫名其妙的要求),可以退而求其次,用当前系统编码决定文件内的编码: 
import locale 
import string 
import re 
#根据当前系统的encoding构造需要的编码取值 
lang = string.upper(locale.setlocale(locale.LC_ALL, "")) 
textencoding = None 3 
#检查编码的值是不是满足我们需要的情况 
if re.match("UTF-8", lang) != None: 
# UTF-8编码 
textencoding = "utf-8" 
elif re.match(r"CHINESE|CP936", lang): 
# Windows下的GB编码 
textencoding = "gb18030" 
elif re.match(r"GB2312|GBK|GB18030", lang): 
# Linux下的GB编码 
textencoding = "gb18030" 
else: 
# 其他情况,抛个错误吧 
raise UnicodeError 
fd = file(filename, "r") 
fulltextlist = fd.readlines() 
# 把每一行转换成unicode 
for each in len(fulltextlist): 
fulltextlist[i] = unicode(each, textencoding) 
fd.close() 
# 如果要打印的话,可以用text.encode(encoding)来恢复成多字节编码 
六、异常处理
编码encoding发生在Unicode字符串转换为字节序列时,而解码decoding发生在字节序列转换为Unicode字符串时(encoding always takes a Unicode string and returns a bytes sequence, and decoding always takes a bytes sequence and returns a Unicode string)。 
UnicodeDecodeError 
UnicodeDncodeError通常发生在将str字符串解码为特定Unicode字符串时。由于不同的编码只能映射部分str字符串到对应的Unicode字符,所以遇到一些字符时解码会失败。 
UnicodeEncodeError 
UnicodeEncodeError通常发生在将Unicode字符串编码为特定字节序列时。由于不同的编码只能映射部分Unicode字符到对应的str字符串,所以遇到一些字符时编码会失败。 
处理python编码转换时的UnicodeDecodeError异常 
python提供的unicode转换不像iconv或是mbstowcs之类的方便。如果转换一段unicode("1234中文",'ascii') 到utf8,会直接出现UnicodeDecodeError的错误。如果在你能预知字串符的编码的时候,比如你用unicode('1234中文', 'gbk') 就不会出现错误;不过很多时候,会出现CJK混合的情况,如果要做到将一段CJK文件转换成unicode可能就行不通了。好在python的codecs提供了register_error这个功能: 
register_error(name, error_handler) 
原理很简单,不过要先看unicode是如何处理异常的。unicode这个函数是将一段string按输入的编码转换成目标的编码,如果出现了不与输入编码相符的,会出现一个UnicodeDecodeError的异常,通常有三种处理方法:strict、replace、ignore;默认是 strict,就是直接raise UnicodeDecodeError。通过register_error,我们也可以有自己的处理方法,如果遇到与输入的编码不符的时候,我们就自己识别,比如GBK、BIG5、JP的字符。 
def cjk_replace(exc): 
if not isinstance(exc, UnicodeDecodeError): 
raise TypeError("don't know how to handle %r" % exc) 
if exc.end + 1 > len(exc.object): 
raise TypeError('unknown codec ,the object too short!') 
ch1 = ord(exc.object[exc.start:exc.end]) 
newpos = exc.end + 1 
ch2 = ord(exc.object[exc.start + 1:newpos]) 
sk = exc.object[exc.start:newpos] 
if 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0x7E<=ch2<=0xFE): # GBK 
return (unicode(sk,'cp936'), newpos) 
if 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0xA1<=ch2<=0xFE): # BIG5 
return (unicode(sk,'big5'), newpos) 
raise TypeError('unknown codec !') 
codecs.register_error("cjk_replace", cjk_replace) 
我们的cjk_replace现在只能处理GBK与BIG5的,因为我对编码也不是特别了解,只是大概知道GBK与BIG5的,不太了解JP的。在 cjk_replace这个函数里,我们对不认识的文字进行手工识别,如果认识的编码,就用正确的方法,并返回编码后的内容与新的pos,比如“1234中文”,在pos为4的时候,会调用我们的cjk_replace,我们会返回一个从gbk转换成utf8的“中”字,并返回下个正确的位置“文”的起始位置。当然了,处理“文”的时候,还会再调用一次。下面看看是如何使用的: 
filedata = open('test.txt','r).read() #gbk and big5 file 
data = unicode(filedata,'ascii','cjk_replace').encode('utf8') 
小结 
一个比较一般的Python中文处理的流程: 
* 将欲处理的字符串用unicode函数以正确的编码转换为Unicode 
* 在程序中统一用Unicode字符串进行操作 
* 输出时,使用encode方法,将Unicode再转换为所需的编码 
有几点要说明一下: 
* 所谓“正确的”编码,指得是指定编码和字符串本身的编码必须一致。这个其实并不那么容易判断,一般来说,我们直接输入的简体中文字符,有两种可能的编码:GB2312(GBK、GB18030)、以及UTF-8 
* encode成本地编码的时候,必须要保证目标编码中存在欲转换字符的内码。encode这种操作一般是通过一个本地编码对应Unicode的编码转换表来进行的,事实上每个本地编码只能映射到Unicode的一部分。但是映射的区域是不同的,比如Big-5对应的Unicode的编码范围和 GBK对应的就不一样(实际上这两个编码有部分范围是重叠的)。所以,Unicode的一些字符(比如本身就是从GB2312转换来的那些),可以映射到 GBK,但未必可以映射到Big-5,如果你想转换到Big-5,很有可能就会出现编码找不到的异常。但UTF-8的码表范围实际上和Unicode是一样的(只是编码形式不同而已),所以,理论上来说,任何本地编码的字符,都可以被转换到UTF-8 
* GB2312、GBK、GB18030本质上是同一种编码标准。只是在前者的基础上扩充了字符数量 
* UTF-8和GB编码不兼容 

* 出现编解码异常时可能需要自己编写编解码解析函数,这需要了解一些字符编码的知识

七、最后附一些关于 python 编码问题的总结:

1、写的代码模块需要指定编码
   如果代码没有指定coding,python就默认所有的字符为ASCII码,
   ASCII码只支持256个字符,ASCII码不支持中文,所以就报错。
   所以要在代码前写上#coding:utf-8或#coding:gbk
   但通用写上#coding:utf-8

2、python2内部所有编码统一为unicode
  unicode可以处理世界上所有语言的字符。
  utf-8为unicode的一种实现形式,所以需要在代码前写上#coding:utf-8

3、编码转换
   牢记python2内部编码为unicode.
   其它的编码decode()为unicode,再编码encode()为你指定的编码,就不会出现乱码。

4、网页采集时
  代码指定#coding:utf-8
  如果网页的编码为gbk
  需要这样处理:
  html = html.decode('gbk').encode('utf-8')

5、代码前也可以写#coding:gbk,但也要保证你的代码文件的保存格式为gbk.这个在windos下会出现这样的问题。

6、字典等key或值的汉字问题
#coding:utf-8
dict1 ={1:'python周末培训班',2:'咨询010-68165761 QQ:1465376564'}

print dict1 
# 这样输出的没有显示汉字,是显示汉字的其它编码

dict2 ={1:'python视频培训班',2:'咨询010-68165761 QQ:1465376564'}
for key in dict2:
    print dict2[key]

7、unicode的汉字编码写到文本文件中
  需要根据文本文件的编码进行转换
  可以encode('utf-8')或encode('gbk')

总结:凡是报错信息中出现的错误包含“ASCII”,就是没有指定汉字编码的问题。

参考资料 
1、  http://bbs3.chinaunix.net/thread-1389703-1-2.html
2、 Python的中文处理及其它 
http://www.go4pro.org/?p=38
3、 Python处理中文的时候的一些小技巧 
http://cocre.com/?p=461
4、 Unicode In Python, Completely Demystified. Kumar McMillan 
http://farmdev.com/talks/unicode
5、 python中文处理好方法 
http://www.okpython.com/bbs/viewthread.php?tid=311
6、 Python的中文处理 
http://hi.baidu.com/mrsz/blog/item/7812a5018c2cf2031d9583d2.html
7、 UnicodeDecodeError 
http://wiki.python.org/moin/UnicodeDecodeError
8、 UnicodeEncodeError 
http://wiki.python.org/moin/UnicodeEncodeError
9、 如何处理python编码转换时的UnicodeDecodeError异常 
http://blog.chinaunix.net/u/8873/showart_1009737.html
10、codecs — Codec registry and base classes 

http://docs.python.org/library/codecs.html

11、python 中文乱码 问题深入分析

http://blog.csdn.net/kiki113/article/details/4062063

12、python新手必碰到的问题---encode与decode,中文乱码[转]

http://www.51testing.com/?uid-524463-action-viewspace-itemid-817888

13、Python 编码和 Unicode(深入理解 python 编码的文章,推荐阅读

http://blog.jobbole.com/50345/

相关文章推荐

python中文字符串编码处理

1。字符串还是字节串?我认为,python的字符串只能说是字节串,你甚至可以在里面存放一张图片或者一个二进制可执行文件import types f=open(‘d://hello.jpg’,'r’)...

【转】Python urllib2.urlopen打开中文url的编码处理

http://hi.baidu.com/andimeo/item/4eec9ad4dc25622a38f6f736 考文章:http://lijiang.javaeye.com/blog/3...

也谈 Python 的中文编码处理

转 原地址  http://in355hz.iteye.com/blog/1860787 最近业务中需要用 Python 写一些脚本。尽管脚本的交互只是命令行 + ...

python中读写文件及中文编码处理方法

一、打开文件 代码如下: >>> f = open("d:\test.txt", "w") 说明: 第一个参数是文件名称,包括路径;第二个参数是打开的模式mode 'r'...

SyntaxError:Non-ASCII character ,……no encoding declared. --python 中文处理的编码问题

http://blog.chinaunix.net/u/3204/showart_389639.html http://www.woodpecker.org.cn/diveintopython/...
  • shun01
  • shun01
  • 2014-03-13 16:15
  • 1109

Python2 中文编码处理

今天写了几个脚本,都遇到了中英文混编的情况。需求要将其中的中文标点符号切换为英文符号。 举个例子: tags = '你好,good, 国语' 要将其中的中文半角逗号替换为英文逗号,为了方便后续的...

Python学习01-中文编码

Python 简介Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有...

python 中文编码问题

转自:http://hi.baidu.com/fc_lamp/blog/item/177d8f835b1bd0bd0df4d273.html python 中文编码问题 中文编码问题是用中文的程序...

Python基础教程(一):简介、环境搭建、中文编码

Python 基础教程 Python是一种解释型、面向对象、动态数据类型的高级程序设计语言。 Python由Guido van Rossum于1989年底发明,第一个公开发行版发行于1991年。 像...

python的中文编码问题

我很想就这个问题做个详细的解释,但也许水平有限,解释的不是很清楚,甚至自己理解的都不正确,那么我将长期修改 我使用pyscripter来作为编辑器,首先在pyscriter上打上这么一行代码   运行...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)