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的源码浅要剖析Python的内存管理
Apr 16 Python
Python实现excel转sqlite的方法
Jul 17 Python
从运行效率与开发效率比较Python和C++
Dec 14 Python
Python编写合并字典并实现敏感目录的小脚本
Feb 26 Python
python flask框架实现传数据到js的方法分析
Jun 11 Python
Jupyter Notebook的连接密码 token查询方式
Apr 21 Python
浅谈Python中文件夹和python package包的区别
Jun 01 Python
python读取excel进行遍历/xlrd模块操作
Jul 12 Python
如何利用Python matplotlib绘制雷达图
Dec 21 Python
python学习之panda数据分析核心支持库
May 07 Python
PyTorch 如何设置随机数种子使结果可复现
May 12 Python
Python GUI编程之tkinter 关于 ttkbootstrap 的使用详解
Mar 03 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获取后台Job管理的实现代码
2011/06/10 PHP
js资料prototype 属性
2007/03/13 Javascript
JS 如果改变span标签的是否隐藏属性
2011/10/06 Javascript
jquery.ui.draggable中文文档(原文翻译)
2013/11/15 Javascript
将字符串中由空格隔开的每个单词首字母大写
2014/04/06 Javascript
jQuery层级选择器用法分析
2015/02/10 Javascript
jQuery对象和DOM对象之间相互转换的方法介绍
2015/02/28 Javascript
jQuery解析XML与传统JavaScript方法的差别实例分析
2015/03/05 Javascript
无阻塞加载js,防止因js加载不了影响页面显示的问题
2016/12/18 Javascript
jQuery选择器之子元素过滤选择器
2017/09/28 jQuery
使用socket.io制做简易WEB聊天室
2018/01/02 Javascript
vue的token刷新处理的方法
2018/07/17 Javascript
大转盘抽奖小程序版 转盘抽奖网页版
2020/04/16 Javascript
详解关于表格合并span-method方法的补充(表格数据由后台动态返回)
2019/05/21 Javascript
利用JavaScript为句子加标题的3种方法示例
2021/01/05 Javascript
python去掉字符串中重复字符的方法
2014/02/27 Python
Python制作简单的网页爬虫
2015/11/22 Python
遗传算法python版
2018/03/19 Python
Python装饰器知识点补充
2018/05/28 Python
tensorflow 加载部分变量的实例讲解
2018/07/27 Python
python 读取Linux服务器上的文件方法
2018/12/27 Python
Python selenium根据class定位页面元素的方法
2019/02/26 Python
Python3.5基础之函数的定义与使用实例详解【参数、作用域、递归、重载等】
2019/04/26 Python
利用matplotlib实现根据实时数据动态更新图形
2019/12/13 Python
详细分析Python可变对象和不可变对象
2020/07/09 Python
浅谈Python里面None True False之间的区别
2020/07/09 Python
简单总结CSS3中视窗单位Viewport的常见用法
2016/02/04 HTML / CSS
Can a struct inherit from another class? (结构体能继承类吗)
2014/07/22 面试题
优秀毕业大学生推荐信
2013/11/13 职场文书
纪念建党演讲稿范文
2014/01/13 职场文书
机械设计及其自动化专业求职信
2014/06/09 职场文书
生物学专业求职信
2014/07/23 职场文书
学校食品安全责任书
2015/01/29 职场文书
小学教师求职信范文
2015/03/20 职场文书
预备党员表决心的话
2015/09/22 职场文书
PHP判断是否是json字符串
2021/04/01 PHP