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 相关文章推荐
如何处理Python3.4 使用pymssql 乱码问题
Jan 08 Python
python学习之matplotlib绘制散点图实例
Dec 09 Python
python os.path模块常用方法实例详解
Sep 16 Python
详解Python Matplotlib解决绘图X轴值不按数组排序问题
Aug 05 Python
解决python 3 urllib 没有 urlencode 属性的问题
Aug 22 Python
Centos7下源码安装Python3 及shell 脚本自动安装Python3的教程
Mar 07 Python
python输出数学符号实例
May 11 Python
python批量修改交换机密码的示例
Sep 22 Python
基于Python的接口自动化读写excel文件的方法
Jan 15 Python
python压包的概念及实例详解
Feb 17 Python
Python OpenCV超详细讲解调整大小与图像操作的实现
Apr 02 Python
如何基于python实现单目三维重建详解
Jun 25 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
php IP转换整形(ip2long)的详解
2013/06/06 PHP
Memcached常用命令以及使用说明详解
2013/06/27 PHP
浅谈ThinkPHP中initialize和construct的区别
2017/04/01 PHP
PHP对称加密算法(DES/AES)类的实现代码
2017/11/14 PHP
ExtJs grid行 右键菜单的两种方法
2010/06/19 Javascript
JS基础之undefined与null的区别分析
2011/08/08 Javascript
flash遮住div问题的正确解决方法
2014/02/27 Javascript
JS+CSS实现表格高亮的方法
2015/08/05 Javascript
jquery分页插件jquery.pagination.js使用方法解析
2016/04/01 Javascript
Javascript中call,apply,bind方法的详解与总结
2016/12/12 Javascript
JS实现的驼峰式和连字符式转换功能分析
2016/12/21 Javascript
JS实现禁止用户使用Ctrl+鼠标滚轮缩放网页的方法
2017/04/28 Javascript
js用类封装pop弹窗组件
2017/10/08 Javascript
微信小程序时间控件picker view使用详解
2018/12/28 Javascript
使用Vue实现移动端左滑删除效果附源码
2019/05/16 Javascript
微信小程序tabBar 返回tabBar不刷新页面
2019/07/25 Javascript
js实现简单页面全屏
2019/09/17 Javascript
vue.js实现图书管理功能
2019/09/24 Javascript
如何在JavaScript中等分数组的实现
2020/12/13 Javascript
Python开发编码规范
2006/09/08 Python
Python实现批量把SVG格式转成png、pdf格式的代码分享
2014/08/21 Python
python版本的仿windows计划任务工具
2018/04/30 Python
python 日期操作类代码
2018/05/05 Python
Django shell调试models输出的SQL语句方法
2019/08/29 Python
通过Canvas及File API缩放并上传图片完整示例
2013/08/08 HTML / CSS
捷克多品牌在线时尚商店:ANSWEAR.cz
2020/10/03 全球购物
总经理岗位职责范本
2014/02/02 职场文书
个人简历自我评价范文
2014/02/04 职场文书
物流管理专业推荐信
2014/09/06 职场文书
大学感恩节活动策划方案
2014/10/11 职场文书
云南省召开党的群众路线教育实践活动总结会议新闻稿
2014/10/21 职场文书
2014年惩防体系建设工作总结
2014/12/01 职场文书
2014年医务科工作总结
2014/12/18 职场文书
SQL Server代理:理解SQL代理错误日志处理方法
2021/06/30 SQL Server
Java 超详细讲解IO操作字节流与字符流
2022/03/25 Java/Android
关于mysql中string和number的转换问题
2022/06/14 MySQL