python 中文乱码问题深入分析


Posted in Python onMarch 13, 2011

在本文中,以'哈'来解释作示例解释所有的问题,“哈”的各种编码如下:
1. UNICODE (UTF8-16),C854;
2. UTF-8,E59388;
3. GBK,B9FE。
一、python中的str和unicode
一直以来,python中的中文编码就是一个极为头大的问题,经常抛出编码转换的异常,python中的str和unicode到底是一个什么东西呢?
在python中提到unicode,一般指的是unicode对象,例如'哈哈'的unicode对象为
u'\u54c8\u54c8'
而str,是一个字节数组,这个字节数组表示的是对unicode对象编码(可以是utf-8、gbk、cp936、GB2312)后的存储的格式。这里它仅仅是一个字节流,没有其它的含义,如果你想使这个字节流显示的内容有意义,就必须用正确的编码格式,解码显示。
例如:
python 中文乱码问题深入分析

对于unicode对象哈哈进行编码,编码成一个utf-8编码的str-s_utf8,s_utf8就是是一个字节数组,存放的就是'\xe5\x93\x88\xe5\x93\x88',但是这仅仅是一个字节数组,如果你想将它通过print语句输出成哈哈,那你就失望了,为什么呢?

因为print语句它的实现是将要输出的内容传送了操作系统,操作系统会根据系统的编码对输入的字节流进行编码,这就解释了为什么utf-8格式的字符串“哈哈”,输出的是“????”,因为 '\xe5\x93\x88\xe5\x93\x88'用GB2312去解释,其显示的出来就是“????”。这里再强调一下,str记录的是字节数组,只是某种编码的存储格式,至于输出到文件或是打印出来是什么格式,完全取决于其解码的编码将它解码成什么样子。

这里再对print进行一点补充说明:当将一个unicode对象传给print时,在内部会将该unicode对象进行一次转换,转换成本地的默认编码(这仅是个人猜测)

二、str和unicode对象的转换

str和unicode对象的转换,通过encode和decode实现,具体使用如下:

python 中文乱码问题深入分析

将GBK'哈哈'转换成unicode,然后再转换成UTF8

三、Setdefaultencoding

python 中文乱码问题深入分析

如上图的演示代码所示:

当把s(gbk字符串)直接编码成utf-8的时候,将抛出异常,但是通过调用如下代码:

import sys

reload(sys)

sys.setdefaultencoding('gbk')

后就可以转换成功,为什么呢?在python中str和unicode在编码和解码过程中,如果将一个str直接编码成另一种编码,会先把str解码成unicode,采用的编码为默认编码,一般默认编码是anscii,所以在上面示例代码中第一次转换的时候会出错,当设定当前默认编码为'gbk'后,就不会出错了。

至于reload(sys)是因为Python2.5 初始化后会删除 sys.setdefaultencoding 这个方法,我们需要重新载入

四、操作不同文件的编码格式的文件

建立一个文件test.txt,文件格式用ANSI,内容为:

abc中文

用python来读取

# coding=gbk

print open("Test.txt").read()

结果:abc中文

把文件格式改成UTF-8:

结果:abc涓???

显然,这里需要解码:

# coding=gbk

import codecs

print open("Test.txt").read().decode("utf-8")

结果:abc中文

上面的test.txt我是用Editplus来编辑的,但当我用Windows自带的记事本编辑并存成UTF-8格式时,

运行时报错:

Traceback (most recent call last):

File "ChineseTest.py", line 3, in 

print open("Test.txt").read().decode("utf-8")

UnicodeEncodeError: 'gbk' codec can't encode character u'\ufeff' in position 0: illegal multibyte sequence

原来,某些软件,如notepad,在保存一个以UTF-8编码的文件时,会在文件开始的地方插入三个不可见的字符(0xEF 0xBB 0xBF,即BOM)。

因此我们在读取时需要自己去掉这些字符,python中的codecs module定义了这个常量:

# coding=gbk

import codecs

data = open("Test.txt").read()

if data[:3] == codecs.BOM_UTF8:

data = data[3:]

print data.decode("utf-8")

结果:abc中文

五、文件的编码格式和编码声明的作用

源文件的编码格式对字符串的声明有什么作用呢?这个问题困扰一直困扰了我好久,现在终于有点眉目了,文件的编码格式决定了在该源文件中声明的字符串的编码格式,例如:

str = '哈哈'

print repr(str)

a.如果文件格式为utf-8,则str的值为:'\xe5\x93\x88\xe5\x93\x88'(哈哈的utf-8编码)

b.如果文件格式为gbk,则str的值为:'\xb9\xfe\xb9\xfe'(哈哈的gbk编码)

在第一节已经说过,python中的字符串,只是一个字节数组,所以当把a情况的str输出到gbk编码的控制台时,就将显示为乱码:????;而当把b情况下的str输出utf-8编码的控制台时,也将显示乱码的问题,是什么也没有,也许'\xb9\xfe\xb9\xfe'用utf-8解码显示,就是空白吧。>_<

说完文件格式,现在来谈谈编码声明的作用吧,每个文件在最上面的地方,都会用# coding=gbk 类似的语句声明一下编码,但是这个声明到底有什么用呢?到止前为止,我觉得它的作用也就是三个:

  1. 声明源文件中将出现非ascii编码,通常也就是中文;
  2. 在高级的IDE中,IDE会将你的文件格式保存成你指定编码格式。
  3. 决定源码中类似于u'哈'这类声明的将‘哈'解码成unicode所用的编码格式,也是一个比较容易让人迷惑的地方,看示例:

#coding:gbk

ss = u'哈哈'

print repr(ss)

print 'ss:%s' % ss

将这个些代码保存成一个utf-8文本,运行,你认为会输出什么呢?大家第一感觉肯定输出的肯定是:

u'\u54c8\u54c8'

ss:哈哈

但是实际上输出是:

u'\u935d\u581d\u6431'

ss:????

为什么会这样,这时候,就是编码声明在作怪了,在运行ss = u'哈哈'的时候,整个过程可以分为以下几步:

1) 获取'哈哈'的编码:由文件编码格式确定,为'\xe5\x93\x88\xe5\x93\x88'(哈哈的utf-8编码形式)

2) 转成 unicode编码的时候,在这个转换的过程中,对于'\xe5\x93\x88\xe5\x93\x88'的解码,不是用utf-8解码,而是用声明编码处指定的编码GBK,将'\xe5\x93\x88\xe5\x93\x88'按GBK解码,得到就是''????'',这三个字的unicode编码就是u'\u935d\u581d\u6431',至止可以解释为什么print repr(ss)输出的是u'\u935d\u581d\u6431' 了。

好了,这里有点绕,我们来分析下一个示例:

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

ss = u'哈哈'

print repr(ss)

print 'ss:%s' % ss

将这个示例这次保存成GBK编码形式,运行结果,竟然是:

UnicodeDecodeError: 'utf8' codec can't decode byte 0xb9 in position 0: unexpected code byte

这里为什么会有utf8解码错误呢?想想上个示例也明白了,转换第一步,因为文件编码是GBK,得到的是'哈哈'编码是GBK的编码'\xb9\xfe\xb9\xfe',当进行第二步,转换成 unicode的时候,会用UTF8对'\xb9\xfe\xb9\xfe'进行解码,而大家查utf-8的编码表会发现,utf8编码表(关于UTF- 8解释可参见字符编码笔记:ASCII、UTF-8、UNICODE)中根本不存在,所以会报上述错误。

Python 相关文章推荐
详解Django框架中用户的登录和退出的实现
Jul 23 Python
Python计算斗牛游戏概率算法实例分析
Sep 26 Python
python 将字符串转换成字典dict的各种方式总结
Mar 23 Python
pycharm 取消默认的右击运行unittest的方法
Nov 29 Python
实例讲解Python脚本成为Windows中运行的exe文件
Jan 24 Python
Python中如何将一个类方法变为多个方法
Dec 30 Python
Python实现结构体代码实例
Feb 10 Python
python3 使用traceback定位异常实例
Mar 09 Python
Django静态资源部署404问题解决方案
May 11 Python
python实现学生管理系统开发
Jul 24 Python
如何使用Python调整图像大小
Sep 26 Python
基于python获取本地时间并转换时间戳和日期格式
Oct 27 Python
学习python处理python编码问题
Mar 13 #Python
布同 Python中文问题解决方法(总结了多位前人经验,初学者必看)
Mar 13 #Python
布同 统计英文单词的个数的python代码
Mar 13 #Python
python将多个文本文件合并为一个文本的代码(便于搜索)
Mar 13 #Python
布同自制Python函数帮助查询小工具
Mar 13 #Python
Python中的文件和目录操作实现代码
Mar 13 #Python
python 中的列表解析和生成表达式
Mar 10 #Python
You might like
php 检查电子邮件函数(自写)
2014/01/16 PHP
PHP/HTML混写的四种方式总结
2017/02/27 PHP
PHP实现中国公民身份证号码有效性验证示例代码
2017/05/03 PHP
PHP开发之用微信远程遥控服务器
2018/01/25 PHP
PHP getDocNamespaces()函数讲解
2019/02/03 PHP
Laravel 模型关联基础教程详解
2019/09/17 PHP
js鼠标左右键 键盘值小结
2010/06/11 Javascript
给文字加上着重号的JS代码
2013/11/12 Javascript
jQuery Uploadify 上传插件出现Http Error 302 错误的解决办法
2015/12/12 Javascript
Extjs让combobox写起来简洁又漂亮
2017/01/05 Javascript
详解react服务端渲染(同构)的方法
2017/09/21 Javascript
详解Node使用Puppeteer完成一次复杂的爬虫
2018/04/18 Javascript
使用javascript函数编写简单银行取钱存钱流程
2018/05/26 Javascript
微信小程序仿今日头条导航栏滚动解析
2019/08/20 Javascript
js实现拖动缓动效果
2020/01/13 Javascript
Antd的Table组件嵌套Table以及选择框联动操作
2020/10/24 Javascript
Python Web框架Pylons中使用MongoDB的例子
2013/12/03 Python
python中使用urllib2获取http请求状态码的代码例子
2014/07/07 Python
Python os模块中的isfile()和isdir()函数均返回false问题解决方法
2015/02/04 Python
详细解析Python中的变量的数据类型
2015/05/13 Python
Python递归遍历列表及输出的实现方法
2015/05/19 Python
Python3.遍历某文件夹提取特定文件名的实例
2018/04/26 Python
实践Vim配置python开发环境
2018/07/02 Python
Flask框架各种常见装饰器示例
2018/07/17 Python
python实现将range()函数生成的数字存储在一个列表中
2020/04/02 Python
Django DRF路由与扩展功能的实现
2020/06/03 Python
解决pytorch下出现multi-target not supported at的一种可能原因
2021/02/06 Python
详解canvas.toDataURL()报错的解决方案全都在这了
2020/03/31 HTML / CSS
Jabra捷波朗美国官网:用于办公、车载和运动的无线蓝牙耳麦
2017/02/01 全球购物
JACK & JONES荷兰官网:男士服装和鞋子
2021/03/07 全球购物
服装设计专业毕业生推荐信
2013/11/09 职场文书
酒店个人培训自我鉴定
2013/12/11 职场文书
公职人员索取回扣检举信
2014/04/04 职场文书
请假条标准格式规范
2014/04/10 职场文书
委托协议书范本
2014/04/22 职场文书
迎新春趣味活动方案
2014/08/24 职场文书