python中文乱码不着急,先看懂字节和字符


Posted in Python onDecember 20, 2017

Python2.x使用过程中,中文乱码解决最耳熟能详的方法就是在代码前加上#-*- coding:utf-8 ?*-

那么为什么需要这么做呢?什么又是字节和字符?下面我们了解下。

我来讲一下字符问题我的理解吧,虽然我对Python的编码处理的具体细节还不太清楚,不过临时稍微看了一下,和Perl的原理也差不多

最重要的是必须区分“字符”和“字节”的不同,“字符”是抽象的,而“字节”是具体的

比如一个“中”字,在不同编码中用如下字节表示:

    GBK      Big5        UTF-8     UTF-16LE 
\xD6\xD0  \xA4\xA4  \xE4\xB8\xAD  \x2D\x4E 

所谓“抽象”的“字符”的“中”,并不是指“\xD6\xD0”或“\xA4\xA4”或任何字节,应该把它理解成:GBK编码中“\xD6\xD0”字节所指代的那个字符(语言学中的能指→所指),或者UTF-8编码中“\xE4\xB8\xAD”所指代的那个字符,但并不是这些具体字节本身

问题是,抽象的字符要作为数据进行存储和传递,就必须有具体的形式,也就是说你在程序内部实现中,要存储“中”这个字符,你必须采用某些特定的字节。你可以用“\xD6\xD0”,也可以用“\xE4\xB8\xAD”,也可以用“\x2D\x4E”,Python在Windows下采用的是UTF-16LE(?),也就意味着它的“字符”的载体编码是UTF-16LE

sys.setdefaultencoding(name) 
Set the current default string encoding used by the Unicode implementation.

文档上是这么写的,如果我的理解没错的话,这个函数的作用就是改变“字符”的载体编码,sys.setdefaultencoding('gbk')以后,“中”这个字符在程序内部就不是用“\x2D\x4E”来承载,而是用“\xD6\xD0”来承载了

Python2.x里的str和unicode有什么区别呢?从字面意义上看容易混淆,实际上,你可以把它理解成str是“字节串”,unicode是“字符串”(string总是翻译成“字符串”,在这里就很容易把人绕晕),看下面的例子:

# -*- coding: gb2312 -*- 
 
s = "张三李四" 
print len(s) #=> 8 
u = s.decode('gbk') 
print len(u) #=> 4

我的脚本编码用的是GBK,而不是UTF-8,你会看到len(s)是8,这是这四个汉字所用的实际8个“字节”,而len(u)是4,这就表示这里有4个“字符”

encode和decode是什么意思呢?所谓编码,就是把意义转换成符号;而解码,就是把符号还原成意义。在这里,encode应该理解成把抽象的字符转换成具体的字节,而decode是把具体的字节还原成抽象的字符

现在的问题是:str类和unicode类都同时具有encode和decode方法,这是一个让我很不以为然的设定。如果按照字节与字符的区分,encode方法是应该只归unicode类所有,decode方法是只归str类所有的,因为“意义”只能转换成“符号”,“意义”再还原成“意义”这本身就没有意义。

假如我们这样:

# -*- coding: gb2312 -*- 
 
s = "张三李四" 
u = s.decode('gbk') # 没问题,字节解码为字符,符号还原为意义 
s2 = s.encode('gbk') 
 # 出错了!字节没法再编码成字节,除非s全部是ASCII字符,但是这样s2和s是完全等同的,这个操作有什么意义? 
u2 = u.decode('gbk') 
 # 又出错了!也只能u只包含ASCII字符,u2和u也是完全等同,这个操作也没有意义

在这里提一下Perl的处理方式,我不知道Python处理编码的原理是否是直接得自Python,还是说这是各门语言共同的做法(但是Ruby又不是这样做的),总之Python2.x是有缺陷的

Perl里只有一种string,它实际也区分字符串和字节串(以UTF-8作为底层的承载编码),但不像Python2.x分str和unicode,而是string内部有一个utf8的flag,这个flag是on的时候,这个string就是一个“字符”串,这个flag是off的时候就是一个“字节”串,它的编码、解码函数如下:

$octets = encode(ENCODING, $string [, CHECK])
$string = decode(ENCODING, $octets [, CHECK])

$octets就是字节串,$string就是字符串,也就是说,encode只对$string起作用,而decode只对$octets起作用,不像Python是str和unicode两类两个方法都有,但是其实各有一个是没用的。LarryWall是语言学家,他设计的这一套字符、字节关系是完全符合语言学中的“能指-所指”理论的,而GvR恐怕就对语言学不在行了,Python的处理就不怎么精妙了。

再来说一下file.write为什么有编码问题:

# -*- coding: gb2312 -*- 
s = "张三李四" 
u = s.decode('gbk') 
 
f = open('text.txt','w') 
f.write(u) # 出错! 
f.write(u.encode('gbk')) # 这样才行

出错的原因很简单,你想输出的是“字符”,而不是“字节”。上面说过,“字符”是抽象的,你是没有办法把一个抽象的东西写到文件里去的。虽然抽象的字符下面肯定是有具体的承载字节的,但是Python似乎并不愿意把unicode底层的字节跟IO搅在一起,这就导致f.write(a_unicode)的失败,当然a_unicode假如只包含ASCII字符,这个可以成功,然而这是一种捷径,是一条让人越来越糊涂的捷径

然后再是u标记的意义是什么?很简单,就是自动完成字节→字符的转换

# -*- coding: gb2312 -*- 
 
s_or_u1 = "张三李四" 
print type(s_or_u1) #=> <type 'str'> 
 
s_or_u2 = u"张三李四" 
print type(s_or_u2) #=> <type 'unicode'>

u"张三李四"就相当于"张三李四".decode(a_enc),这里的a_enc就是#coding行设定的gb2312

不得不说,(不知是不是从Perl得来的)这套字符处理方式很晦涩,字符、字节区分的概念实在不太容易理解,而Python本身的细节处理也没有做好,Perl做得很干净了,都不容易理解,Python没做干净更不行了。另外再附赠简单介绍Ruby的字符处理方式,跟Perl完全不同:

Ruby中没有字符、字节的区分,一切字符串都是“带有一个编码属性的字节串”。因为没有抽象的字符,所以就没有字节→字符的转换,也就根本没有、也不需要decode方法,Ruby的String类只有encode方法。因为没有抽象的“字符”概念,Ruby的编码问题应该比Perl、Python容易理解。没有“字符”的还有一个好处是:处理多字节文本无需经过中间转换。你要在Perl里处理中文字符,来源文件是GBK编码的,实际都得先转换成UTF-8,Perl才能处理:Python要先转化成UTF-16才能处理。对于海量文本来说,这一转换过程肯定是要耗费一定的资源的。而Ruby不需要这种转换,直接就能处理GBK或其他编码了。可能这样做也是考虑了日文的实际,日文的shift-jis(?)是本土编码,根本都不跟ASCII兼容,不像GBK是跟ASCII兼容的,这样做就不必转换就能处理土著编码的文档了。如果说Perl的字符-字节区分是语言学家的学院派做法的话,Ruby就是契合了多字节字符处理需要的实用派做法。

总结

以上就是本文关于python中文乱码不着急,先看懂字节和字符的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

Python 相关文章推荐
用Python展示动态规则法用以解决重叠子问题的示例
Apr 02 Python
详解Python中的strftime()方法的使用
May 22 Python
Python 实现使用dict 创建二维数据、DataFrame
Apr 13 Python
Django学习教程之静态文件的调用详解
May 08 Python
python3 拼接字符串的7种方法
Sep 12 Python
选择Python写网络爬虫的优势和理由
Jul 07 Python
python读取大文件越来越慢的原因与解决
Aug 08 Python
Python3并发写文件与Python对比
Nov 20 Python
django框架forms组件用法实例详解
Dec 10 Python
解决pyecharts运行后产生的html文件用浏览器打开空白
Mar 11 Python
Python根据指定文件生成XML的方法
Jun 29 Python
基于Python编写一个计算器程序,实现简单的加减乘除和取余二元运算
Aug 05 Python
python决策树之C4.5算法详解
Dec 20 #Python
python 3.6 +pyMysql 操作mysql数据库(实例讲解)
Dec 20 #Python
python实现ID3决策树算法
Dec 20 #Python
理解python中生成器用法
Dec 20 #Python
Python利用turtle库绘制彩虹代码示例
Dec 20 #Python
浅谈Python中range和xrange的区别
Dec 20 #Python
python机器学习实战之树回归详解
Dec 20 #Python
You might like
最贵的咖啡是怎么产生的,它的风味怎么样?
2021/03/04 新手入门
一段防盗连的PHP代码
2006/12/06 PHP
PHP set_time_limit(0)长连接的实现分析
2010/03/02 PHP
jQuery+PHP实现的掷色子抽奖游戏实例
2015/01/04 PHP
PHP策略模式定义与用法示例
2017/07/27 PHP
PHP判断json格式是否正确的实现代码
2017/09/20 PHP
mysqli扩展无法在PHP7下升级问题的解决
2019/09/10 PHP
基于jQuery实现模拟页面加载进度条
2013/04/01 Javascript
js的image onload事件使用遇到的问题
2014/07/15 Javascript
jQuery CSS()方法改变现有的CSS样式表
2014/09/09 Javascript
javascript中eval函数用法分析
2015/04/25 Javascript
利用Vue.js指令实现全选功能
2016/09/08 Javascript
Vue异步组件使用详解
2017/04/08 Javascript
详解React开发中使用require.ensure()按需加载ES6组件
2017/05/12 Javascript
使用jquery的jsonp如何发起跨域请求及其原理详解
2017/08/17 jQuery
原生javascript实现的全屏滚动功能示例
2017/09/19 Javascript
JavaScript解决浮点数计算不准确问题的方法分析
2018/07/09 Javascript
在Vue-cli里应用Vuex的state和mutations方法
2018/09/16 Javascript
vue路由前进后退动画效果的实现代码
2018/12/10 Javascript
vue路由插件之vue-route
2019/06/13 Javascript
JavaScript实现京东放大镜效果
2019/12/03 Javascript
js实现炫酷光感效果
2020/09/05 Javascript
[00:32]2018DOTA2亚洲邀请赛出场——VP
2018/04/04 DOTA
Python编写生成验证码的脚本的教程
2015/05/04 Python
Python中将字典转换为XML以及相关的命名空间解析
2015/10/15 Python
黑科技 Python脚本帮你找出微信上删除你好友的人
2016/01/07 Python
简单掌握Python的Collections模块中counter结构的用法
2016/07/07 Python
Python编程之字符串模板(Template)用法实例分析
2017/07/22 Python
python挖矿算力测试程序详解
2019/07/03 Python
Python中的引用和拷贝实例解析
2019/11/14 Python
Jacques Lemans德国:奥地利钟表品牌
2019/12/26 全球购物
写出SQL四条最基本的数据操作语句(DML)
2012/12/12 面试题
征婚广告词
2014/03/17 职场文书
2014年电话销售工作总结
2014/12/01 职场文书
2016大学先进团支部事迹材料
2016/03/01 职场文书
当你找不到方向的时候,不妨读读刘备的一生
2019/08/05 职场文书