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 相关文章推荐
Python写的服务监控程序实例
Jan 31 Python
python简单实现刷新智联简历
Mar 30 Python
基础的十进制按位运算总结与在Python中的计算示例
Jun 28 Python
python中virtualenvwrapper安装与使用
May 20 Python
python中scikit-learn机器代码实例
Aug 05 Python
利用python GDAL库读写geotiff格式的遥感影像方法
Nov 29 Python
python实现图片识别汽车功能
Nov 30 Python
python paramiko利用sftp上传目录到远程的实例
Jan 03 Python
通过代码简单了解django model序列化作用
Nov 12 Python
python制作一个简单的gui 数据库查询界面
Nov 19 Python
python图片灰度化处理的几种方法
Jun 23 Python
pandas求平均数和中位数的方法实例
Aug 04 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使用JSON和将json还原成数组
2015/02/12 PHP
ucenter中词语过滤原理分析
2016/07/13 PHP
php命令行模式代码实例详解
2021/02/26 PHP
Jquery选择器 $实现原理
2009/12/02 Javascript
jQuery validate 中文API 附validate.js中文api手册
2010/07/31 Javascript
往光标所在位置插入值的js代码
2013/09/22 Javascript
JS 去除Array中的null值示例代码
2013/11/20 Javascript
js 与 php 通过json数据进行通讯示例
2014/03/26 Javascript
深入分析javascript中的错误处理机制
2016/07/17 Javascript
jQuery.form.js插件不能解决连接超时(timeout)的原因分析及解决方法
2016/10/14 Javascript
利用fecha进行JS日期处理
2016/11/21 Javascript
EasyUI Combobox设置默认值 获取text的方法
2016/11/28 Javascript
js仿拉勾网首页穿墙广告效果
2017/03/08 Javascript
彻底搞懂JavaScript中的apply和call方法(必看)
2017/09/18 Javascript
JavaScript实现重力下落与弹性效果的方法分析
2017/12/20 Javascript
学习Vue组件实例
2018/04/28 Javascript
详解Webpack多环境代码打包的方法
2018/08/03 Javascript
关于单文件组件.vue的使用
2018/09/20 Javascript
微信小程序时间选择插件使用详解
2018/12/28 Javascript
vue两组件间值传递 $router.push实现方法
2019/05/15 Javascript
javascript设计模式 ? 策略模式原理与用法实例分析
2020/04/21 Javascript
基于ajax及jQuery实现局部刷新过程解析
2020/09/12 jQuery
vue常用高阶函数及综合实例
2021/02/25 Vue.js
python操作sqlite的CRUD实例分析
2015/05/08 Python
Python构造自定义方法来美化字典结构输出的示例
2016/06/16 Python
python特性语法之遍历、公共方法、引用
2018/08/08 Python
python 实现读取csv数据,分类求和 再写进 csv
2020/05/18 Python
Java byte数组操纵方式代码实例解析
2020/07/22 Python
HTML5 visibilityState属性详细介绍和使用实例
2014/05/03 HTML / CSS
Meli Melo官网:名媛们钟爱的英国奢侈手包品牌
2017/04/17 全球购物
与UNIX有关的几个名词
2015/09/17 面试题
岗位职责的构建方法
2014/02/01 职场文书
小学新学期寄语
2014/04/02 职场文书
地球物理学专业推荐信
2014/09/08 职场文书
村党支部书记四风问题个人对照检查材料思想汇报
2014/10/06 职场文书
Qt数据库应用之实现图片转pdf
2022/06/01 Java/Android