python中文编码问题实例详解

发布时间:2020-02-24编辑:脚本学堂
本文介绍了python中文编码的一些问题与解决方法,在python源码中使用中文字符,运行时会有错误,需要在源码的开头部分加入字符编码的声明,需要的朋友参考下。

python中文编码问题

一、使用中文字符
在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'xd6xd0xcexc4'
>>> str = "中文"
>>> str
'xd6xd0xcexc4'
u"中文"只是声明unicode,实际的编码并没有变。这样子就发生变化了:
>>> str = "中文"
>>> str
'xd6xd0xcexc4'
>>> str = str.decode("gb2312")
>>> str
u'u4e2du6587'

更进一步:
 

复制代码 代码示例:
>>> s = '中文'
>>> s.decode('gb2312')
u'u4e2du6587'
>>> 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了

那么声明成u有什么作用呢?
对于Python来说,只要你声明某字符串是Unicode,它就会用Unicode的一套机制对它进行处理。(www.jb200.com 脚本学堂 整理)
比方说,做字符串操作时会动用到内部的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")
一二三

五、三行代码解决编码问题: Python 2.x字符串编码的小结
解决此类问题的方法,在程序开头加上以下几行代码:
 

复制代码 代码示例:
import sys
reload(sys)
sys.setdefaultencoding(“utf-8″)

那么就可解决几乎95%的这种问题, 但是如果想刨根问底的话, 就需要去了解很多东西了.
首先, 这个就是Python语言本身的问题. 因为在Python 2.x的语法中, 默认的str并不是真正意义上我们理解的字符串, 而是一个byte数组, 或者可以理解成一个纯ascii码字符组成的字符串, 与Python 3中的bytes类型的变量对应; 而真正意义上通用的字符串则是unicode类型的变量, 它则与Python 3中的str变量对应. 本来应该用作byte数组的类型, 却被用来做字符串用, 这种看似奇葩的设定是Python 2一直被人诟病的东西, 不过也没有办法, 为了与之前的程序保持兼容。
在Python 2中作为两种字符串类型, str与unicode之间就需要各种转换的方式. 首先是一种显式转换的方式, 就是encode和decode两种方法. 在这里这两货的意思很容易被搞反, 科学的调用方式是:
str --- decode方法 ---> unicode
unicode --- encode方法 ---> str
比如:
 

复制代码 代码示例:
>>> type('x')
<type 'str'>
>>> type('x'.decode('utf-8'))
<type 'unicode'>
>>> type(u'x'.encode('utf-8'))
<type 'str'>
 

这个逻辑是这样的, 对于unicode字符串使用utf-8编码进行编码, 即调用encode('utf-8')方法生成byte数组类型的结果. 相反对于byte数组进行解码, 生成unicode字符串. 这个新手表示理解不能, 不过熟悉了就见怪不怪了.
另外是隐式的转换, 和C语言中的int转double类似, 当一个unicode字符串和一个str字符串进行连接时会默认自动将str字符串转换成unicode类型然后再连接. 而这个时候使用的编码方式则是系统所默认的编码方式. 使用:
 

复制代码 代码示例:
import sys
print sys.getdefaultencoding()

可以得到当前默认的编码方式, 是不是'ascii'? 是的话就恭喜你中彩了~!! 在这个时候如果有以下一行代码就保证会出错:
 

复制代码 代码示例:
>>> x = u'喵'
>>> x
u'u55b5'
>>> y = x.encode('utf-8')
>>> x + y
Traceback (most recent call last):
File "", line 1, in
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
 

x是unicode类型的变量, y是x经过encode后的结果是str类型的变量. x + y时, 首先要将y转换成unicode字符串, 那么使用什么编码格式转换呢, 用utf-8还是gb2312或者还是utf-16? 这个时候就要根据sys.getdefaultencoding()来确定, 而sys.getdefaultencoding()是'ascii'编码, 在ascii字符表中不存在0xe5这种大于128的字符存在, 所以当然报错啦! 通过加入
 

复制代码 代码示例:
import sys
reload(sys)
sys.setdefaultencoding(“utf-8″)

则可以将默认的编码转换格式变成utf-8, 且大多数情况下, 程序中的字符串是通过utf-8来编码的, 所以只要加上以上三行就可以了.

但是有没有觉得, 加上这些会使得代码有些dirty? 咳, 至少对于我来说确实很dirty. 所以我觉得平时写程序的过程中要养成尽量使用显示的转换的习惯, 并且要明确某个函数返回的到底是str还是unicode, 凡是str的主动decode成unicode, 不要将两者混淆掉, 这样写出来的代码才比较干净. 此外还可以在代码最上方加入'from future import unicode_literals'可以默认将用户自定义字符串变成unicode类型. (www.jb200.com 脚本学堂)
最后我想大吼一声Python 2.x中str不是字符串, 而是B♂Y♂T♂E♂数♂组~!!