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中用has_key()方法查找键是否存在的教程
May 21 Python
python中的格式化输出用法总结
Jul 28 Python
深入理解python中的select模块
Apr 23 Python
Python获取当前路径实现代码
May 08 Python
Python3 加密(hashlib和hmac)模块的实现
Nov 23 Python
pyhton列表转换为数组的实例
Apr 04 Python
实例讲解Python爬取网页数据
Jul 08 Python
详解Python发送email的三种方式
Oct 18 Python
Django添加KindEditor富文本编辑器的使用
Oct 24 Python
对python中的*args与**kwgs的含义与作用详解
Aug 28 Python
基于Python的图像阈值化分割(迭代法)
Nov 20 Python
python删除csv文件的行列
Apr 06 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中OR与|| AND与&amp;&amp;的区别总结
2013/10/26 PHP
PHP递归删除目录几个代码实例
2014/04/21 PHP
CodeIgniter记录错误日志的方法全面总结
2016/05/17 PHP
php日期操作技巧小结
2016/06/25 PHP
PHPCMS忘记后台密码的解决办法
2016/10/30 PHP
解决微信授权回调页面域名只能设置一个的问题
2016/12/11 PHP
JS 获取浏览器和屏幕宽高等信息的实现思路及代码
2013/07/31 Javascript
使用javascript实现页面定时跳转总结篇
2013/09/21 Javascript
JavaScript DOM进阶方法
2015/04/13 Javascript
javascript正则表达式基础知识入门
2015/04/20 Javascript
javascript框架设计之浏览器的嗅探和特征侦测
2015/06/23 Javascript
jquery实现的树形目录实例
2015/06/26 Javascript
四种参数传递的形式——URL,超链接,js,form表单
2015/07/24 Javascript
JavaScript实现in-place思想的快速排序方法
2016/08/07 Javascript
AngularJS中$apply方法和$watch方法用法总结
2016/12/13 Javascript
超全面的JavaScript开发规范(推荐)
2017/01/21 Javascript
JS实现最简单的冒泡排序算法
2017/02/15 Javascript
使用jQuery的load方法设计动态加载及解决被加载页面js失效问题
2017/03/01 Javascript
javascript使用btoa和atob来进行Base64转码和解码
2017/03/20 Javascript
基于JavaScript实现弹幕特效
2020/08/27 Javascript
基于JavaScript实现多级菜单效果
2017/07/25 Javascript
3种vue组件的书写形式
2017/11/29 Javascript
基于ssm框架实现layui分页效果
2019/07/27 Javascript
Vue+Typescript中在Vue上挂载axios使用时报错问题
2019/08/07 Javascript
js实现移动端tab切换时下划线滑动效果
2019/09/08 Javascript
vue根据条件不同显示不同按钮的操作
2020/08/04 Javascript
python初学之用户登录的实现过程(实例讲解)
2017/12/23 Python
把pandas转换int型为str型的方法
2019/01/29 Python
python在新的图片窗口显示图片(图像)的方法
2019/07/11 Python
便利店促销方案
2014/02/20 职场文书
幼儿园教师岗位职责
2015/04/02 职场文书
2015年圣诞节寄语
2015/08/17 职场文书
创业计划书之家教中心
2019/09/25 职场文书
Golang 语言控制并发 Goroutine的方法
2021/06/30 Golang
Mysql数据库手动及定时备份步骤
2021/11/07 MySQL
postgreSQL数据库基础知识介绍
2022/04/12 PostgreSQL