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 相关文章推荐
一则python3的简单爬虫代码
May 26 Python
pycharm 使用心得(三)Hello world!
Jun 05 Python
python网络编程学习笔记(一)
Jun 09 Python
Python中操作MySQL入门实例
Feb 08 Python
Python脚本暴力破解栅栏密码
Oct 19 Python
python列表的常用操作方法小结
May 21 Python
Python在图片中插入大量文字并且自动换行
Jan 02 Python
python-opencv 将连续图片写成视频格式的方法
Jan 08 Python
python判断文件夹内是否存在指定后缀文件的实例
Jun 10 Python
django序列化serializers过程解析
Dec 14 Python
使用python matplotlib 画图导入到word中如何保证分辨率
Apr 16 Python
python 基于opencv去除图片阴影
Jan 26 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实现memcache缓存示例讲解
2013/12/04 PHP
php判断电子邮件是否正确方法
2018/12/04 PHP
响应鼠标变换表格背景或者颜色的代码
2009/03/30 Javascript
Mootools 1.2教程 排序类和方法简介
2009/09/15 Javascript
SeaJS入门教程系列之SeaJS介绍(一)
2014/03/03 Javascript
Javascript中的getUTCDay()方法使用详解
2015/06/10 Javascript
js鼠标点击图片切换效果代码分享
2015/08/26 Javascript
基于jQuery 实现bootstrapValidator下的全局验证
2015/12/07 Javascript
jQuery遍历DOM元素与节点方法详解
2016/04/14 Javascript
js实现获取两个日期之间所有日期的方法
2016/06/17 Javascript
javascript获取网页各种高宽及位置的方法总结
2016/07/27 Javascript
JS取数字小数点后两位或n位的简单方法
2016/10/24 Javascript
Three.js基础部分学习
2017/01/08 Javascript
vue组件间通信解析
2017/03/01 Javascript
jQuery实现对网页节点的增删改查功能示例
2017/09/18 jQuery
如何将HTML字符转换为DOM节点并动态添加到文档中详解
2018/08/19 Javascript
js实现省级联动(数据结构优化)
2020/07/17 Javascript
JavaScript如何操作css
2020/10/24 Javascript
Vue ​v-model相关知识总结
2021/01/28 Vue.js
python脚本监控docker容器
2016/04/27 Python
Python利用IPython提高开发效率
2016/08/10 Python
浅析python中SQLAlchemy排序的一个坑
2017/02/24 Python
Python3编程实现获取阿里云ECS实例及监控的方法
2017/08/18 Python
Python2实现的LED大数字显示效果示例
2017/09/04 Python
python通过微信发送邮件实现电脑关机
2018/06/20 Python
深入浅析Python获取对象信息的函数type()、isinstance()、dir()
2018/09/17 Python
python实现彩色图转换成灰度图
2019/01/15 Python
Python3.5 + sklearn利用SVM自动识别字母验证码方法示例
2019/05/10 Python
python3格式化字符串 f-string的高级用法(推荐)
2020/03/04 Python
html5的input的required使用中遇到的问题及解决方法
2018/04/24 HTML / CSS
德国消费电子产品购物网站:Guter Kauf
2020/09/15 全球购物
eHarmony英国:全球领先的认真恋爱约会平台之一
2020/11/16 全球购物
消防安全员岗位职责
2014/03/10 职场文书
工作说明书范文
2014/05/07 职场文书
农民工预备党员思想汇报
2014/09/14 职场文书
2014年学习委员工作总结
2014/11/14 职场文书