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之重回函数
Oct 10 Python
零基础写python爬虫之抓取百度贴吧并存储到本地txt文件改进版
Nov 06 Python
Python实现的txt文件去重功能示例
Jul 07 Python
python xpath获取页面注释的方法
Jan 14 Python
Python对象与引用的介绍
Jan 24 Python
python实现贪吃蛇游戏
Mar 21 Python
Python面向对象编程基础实例分析
Jan 17 Python
关于Tensorflow 模型持久化详解
Feb 12 Python
wxpython自定义下拉列表框过程图解
Feb 14 Python
Python实现对word文档添加密码去除密码的示例代码
Dec 29 Python
微软开源最强Python自动化神器Playwright(不用写一行代码)
Jan 05 Python
python playwrigh框架入门安装使用
Jul 23 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
10个简化PHP开发的工具
2014/12/25 PHP
使用PHP实现下载CSS文件中的图片
2015/12/06 PHP
php cookie工作原理与实例详解
2016/07/18 PHP
Yii2框架RESTful API 格式化响应,授权认证和速率限制三部分详解
2016/11/10 PHP
php之header的不同用法总结(实例讲解)
2017/11/28 PHP
php生成HTML文件的类方法
2019/10/11 PHP
php数值计算num类简单操作示例
2020/05/15 PHP
[原创]来自ImageSee官方 JavaScript图片浏览器
2008/01/16 Javascript
jQuery插件-jRating评分插件源码分析及使用方法
2012/12/28 Javascript
JS实现切换标签页效果实例代码
2013/11/01 Javascript
jquery实现的V字形显示效果代码
2015/10/27 Javascript
如何防止JavaScript自动插入分号
2015/11/05 Javascript
javascript宿主对象之window.navigator详解
2016/09/07 Javascript
jQuery Ztree行政地区树状展示(点击加载)
2016/11/09 Javascript
react-router4 嵌套路由的使用方法
2017/07/24 Javascript
Vue实现typeahead组件功能(非常靠谱)
2017/08/26 Javascript
Node 搭建一个静态资源服务器的实现
2019/05/20 Javascript
React学习之JSX与react事件实例分析
2020/01/06 Javascript
Python 学习笔记
2008/12/27 Python
python代码制作configure文件示例
2014/07/28 Python
Python中pip安装非PyPI官网第三方库的方法
2015/06/02 Python
Python 多进程并发操作中进程池Pool的实例
2017/11/01 Python
Python遍历pandas数据方法总结
2018/02/09 Python
Python实现针对给定单链表删除指定节点的方法
2018/04/12 Python
更新修改后的Python模块方法
2019/03/03 Python
Django 权限认证(根据不同的用户,设置不同的显示和访问权限)
2019/07/24 Python
Python3 shelve对象持久存储原理详解
2020/03/23 Python
python中pyplot基础图标函数整理
2020/11/10 Python
蔻驰美国官网:COACH美国
2016/08/18 全球购物
国际知名设计师时装商店:Coggles
2016/09/05 全球购物
企业总经理岗位职责
2014/02/13 职场文书
大学生社会实践活动总结
2014/07/03 职场文书
党的群众路线教育实践活动对照检查材料
2014/09/22 职场文书
四风问题个人对照检查材料
2014/09/26 职场文书
2014年第四季度入党积极分子思想汇报(十八届四中全会)
2014/11/03 职场文书
TypeScript中条件类型精读与实践记录
2021/10/05 Javascript