【Python】 编码,en/decode函数以及print语句的一些探索
昨天晚上在整理hashlib和hmac模块的时候,又看到了编码这块的内容。越看越觉得之前的理解不对,然后想研究一下自己想出来,但是越陷越深。。总之把昨晚+今天一个上午的这些自己想到的东西写下来
● 几个概念(あくまで是我为了统一本篇中的术语,至于业界是不是这么说我不敢保证。。)
编码: 计算机认识的其实只有二进制数据,而我们之所以能够在计算机上处理自然语言和自然字符主要是因为有一种对应关系可以映射二进制数据和自然字符。这种映射就是广义上的编码。具体到中文上来,计算机发展源于英美,最开始的编码只要记住26个字符以及一些简单的标点等在计算机中如何存储,可是中国汉字那么多该怎么办。于是很多解决方案出现,纵览所有解决方案,其要义都是把一个中文字分解成若干个小字节,通过小字节间的排列组合来实现二进制数据到汉字的映射。这种映射是汉字编码,也是这篇里会提到的狭义上的编码。
编码格式: 不同的编码格式虽然都能实现汉字编码,但是对于同一个汉字内容,其映射的二进制数据是不同的。比如gbk和utf8就是两个不同的编码格式
● 关于unicode,ascii,gbk和utf8
知乎这个回答写得很好
概括一下,最早有的肯定是ascii编码格式。gb2312,gbk等这一批是一个系列的编码格式,从前一个版本的基础上不断扩充编码。他们来自ascii的一脉相承,是中国人民对ascii编码无法支持汉字显示这一问题的解决方案(原有ascii编码格式的映射不变,额外加上汉字编码)。但是如果全世界各种语言都按照自己的标准来进行编码的创作的话,很快就会乱套的。所以某些国际标准组织退出了unicode(Universal Multiple-Octet Coded Character Set,简称UCS),它是一些(甚至可能是所有?)编码方式的并集。除了ascii的内容继续维持不变外,把其他所有字符都进行了国籍标准的编码,意思是不让不同的字符编码成为计算机领域中的巴别塔。
在旧时代的gb系列中,英文字母沿袭了ascii的习惯,一个字母字符占一个字节;而一个汉字字符都被认为是两个英文字符的组合,占两个字节。而到了新时代的unicode之后,包括英文字母和汉字在内的所有字符都被统一认为是一个字符,并被存储成两个字节。这么一来就出现了一个问题,本来美帝的程序员们写一个字只花一个字节的空间,但是如果他们要和国际接轨,用了unicode的话,一个字就得花两个字节,这浪费了很多资源。这也是为什么unicode在很长一段时间里没有得到推广。
后来互联网的兴盛催生了面向传输的UTF(UCS Transformation Format)标准。其中utf-8是比较有名的一种。utf-8的意思是每次以8个位传输数据。utf8是一种所谓“变长的编码格式”,它灵活地运用1-4个字节来存储不同的字符。当字符处于ascii的范围内就用ascii的一个字节来储存,更复杂的字符则按照unicode的规定用更多的字节去存储。可以说是一种对传输成本的妥协。(是不是感觉历史倒退了w)这也在冥冥之中让utf-8和gb系列又像起来了,它们都对ascii原生的那些字符不做处理,而自己再做一些各自的扩展。gb系列是中国特色,针对汉字的,而utf是面向全世界的。这种相似性也体现在程序中,程序中经常感觉,gbk和utf-8好像是同一个级别的东西吧!很显然,unicode的编码和utf-8的编码并不是完全一一对应的,需要一些算法和操作来进行转换。
utf8,UTF-8在程序中和utf-8都是等价的,程序会自动识别,其他带有-等的编码格式名称也都是一个意思。
● print语句的试验
我猜测,当print语句碰到一个unicode目标的时候,会用当前python shell环境的默认编码格式首先对unicode对象进行encode(此时unicode对象已经变成了一个str对象了),然后再以默认编码格式为基础,根据其包含的汉字和编码的对应规则,把这个str对象解释成中文并显示出来。但是当print语句碰到的直接是个str目标的时候,就不管其从unicode转到str时用的编码格式是什么,直接用默认编码格式的对应规则来解释成中文。所以,当unicode对象转换成str时的编码格式和print语句的默认编码格式不一致的时候就会出现乱码现象。比如在cmd的python shell里面:
>>>uni = u'你好' #存入一个unicode对象 >>>print uni 你好 #可以正常显示 >>>uni.encode("gbk") '\xc4\xe3\xba\xc3' #显示的是个str对象了,如果type(uni.encode("gbk"))得到的就是str对象 >>>print uni.encode("gbk") 你好 #可以正常显示,因为在cmd下的pythonshell里默认个编码格式就是gbk >>>uni.encode("utf-8") '\xe4\xbd\xa0\xe5\xa5\xbd' #可以看到,encode用的编码格式不同,编成的字符串也是不同的 >>>print uni.encode("utf-8") 浣犲ソ #乱码,因为用了gbk中汉字和字符串编码格式对应规则去解释了用utf-8编码成的字符串。 #######さらに###### >>>print '\xc4\xe3' #自己写出来的这么个字符串(前面不加r)的话也会被print解释成中文 你 >>>print uni.encode("utf-8").decode("gbk") 浣犲ソ ''' 乱码,而且和上面的乱码一样,这是因为,在uni被utf-8 encode之后,这个对象变成了str对象,是'\xe4\xbd\xa0\xe5\xa5\xbd' 这个。 后来,它又被按照gbk的规则解码,又变回了unicode,但是此时它在内存里的二进制数据已经和最初的uni不一样了。 最初的uni,应该是'\xc4\xe3\xba\xc3'.decode("gbk"),而现在的这个东西,他decode之前的字符串已经变过了。 这么一个东西再拿去print,又把它编码成了gbk格式,相当于前面那步decode没有做,变回了'\xe4\xbd\xa0\xe5\xa5\xbd'。 再解释成汉字,当然就和最开始用uni编码成utf-8格式再解释成汉字的乱码一样了 '''
更多相关文章
- Python——字符格式化
- Python MOOC简单获取网页内容并以html格式保存在本地
- Python数据格式化
- Django i18n:为{% blocktrans %}块推荐的大小和格式?
- Linux 的磁盘格式化、挂载、磁盘检验、df、du、fdisk、free命令
- 常见压缩格式的区别,及 Linux 下的压缩相关指令
- linux 下常用音频格式的转化
- 如何使用ffmpeg将m4v和wmv视频转换为mp4格式?
- Linux第三章 命令基本格式及文件处理命令