19.python的编码有关问题

19.python的编码问题

  在正式说明之前,先给大家一个参考资料:戳这里

  文章的内容参考了这篇资料,并加以总结,为了避免我总结的不够完善,或者说出现什么错误的地方,有疑问的地方大家可以看看上面那篇文章。

  以下说明是针对于python2.x版本,因为在python3.x中默认使用的是Unicode。

  下面开始讲python中的编码问题,首先,我们看看编码有哪些。

  1. ASCII

  ASCII是用一个字节表示字符,而一个字节由八位二进制组成,所以能产生2**8=256种变化,在计算机刚诞生的年代,用来表示大小写的26个英文字母,外加一些符号之类的还是绰绰有余的。这也是python2.x中默认使用的编码,所以在python2.x中默认不能使用中文,除非使用编码声明。

  2. MBCS

  随着时代的发展,ASCII就太够用了,计算机不能只显示英文吧,那样实在太low。此时,大家看到ASCII码中还有没用完的,所以都想占用剩下的部分,但是剩下的部分还是不够,例如我们中文那么多肯定是不够用的,所以此时又扩展了一下,一个字节不行,我就用两个。而又为了兼容ASCII码,有定义了这样一套规则:如果第一个字节是x80以下,则仍然表示ASCII字符;而如果是x80以上,则跟下一个字节一起(共两个字节)表示一个字符,然后跳过下一个字节,继续往下判断。例如GB...和BIG...之类的都遵循这样的规则。

  但是,这样还是实在太乱了,此时IBM跳了出来,大喊一声:这些东西都要统一进行管理!!所以弄出了代码页的概念,将这些字符集都收录了起来,并进行了分页,而这些分页的总称就叫MBCS,例如GBK在936页,所以又叫cp936。而大家都是使用的双字节,所以也称为DBCS。

  但很明显,MBCS里面收集和各样的字符集,但是你不能说你要使用MBCS这个字符集编码,里面存了怎么多种,到底是要用哪种,你不说清楚我总不能随机给你一种吧。所以必须要进行指定,但是这个工作已经由操作系统自己完成了(linux不支持),而操作系统有时根据地区的不同而选择的。例如简体中文版的,就选GBK,其他国家的又会有不同,具体按版本而定。所以,一旦在python的编码声明中使用MBCS/BDCS,在进行过系统或跨地区运行的时候,报错也是在所难免的。所以编码声明中一定要具体的指定,例如我们常用的utf-8,这样就不会因为系统和地区的差异而造成各种编码的错误。

  在windows中,微软又为它起了个别名,叫ANSI,其实就是MBSC,大家知道就好了。

  3.Unicode

  虽然MBSC一定程度上解决了编码混乱的问题,但还是特点的编码只能显示特点的字符。这样要开发一种适配多国语言的程序就变得非常困难,此时人们在想,有没有一种编码能搞到所以的字符。大家研究了一番之后,Unicode就此诞生。干脆大家都不要在ASCII上拓展来拓展去,搞得各种版本如此混乱。以后大家都用两个字节保存算了,这样就有了256*256=65536种字符可以表示了,总归是够用了吧。这就是UCS-2标准了。后来还有人说不够用的,那么干脆翻一倍,用四个字节表示,256**4=4294967296,就算将来表示外星文字也能撑一段时间了吧。当然现在常用的还是UCS-2标准的。

  UCS(Unicode Character Set)还仅仅是字符对应码位的一张表而已(也就是表示字节),比如"汉"这个字的码位是6C49。字符具体如何传输和储存则是由UTF(UCS Transformation Format)来负责(也就是保存字节)。(注意:表示字节≠保存字节,也就是虽然我用了2个字节表示字符,但是我保存的时候不一定就直接保存用来表示的那个字节)

  刚开始都是直接使用UCS的码位来保存,这就是UTF-16,比如,"汉"直接使用x6Cx49保存(UTF-16-BE),或是倒过来使用x49x6C保存(UTF-16-LE)。但美国佬后来不愿意了,我原来用ASCII只有1个字节就能搞到,现在却要两个字节,足足长了一倍呀。一倍是什么概念,四舍五入那是将近一个亿呀真当我磁盘空间不用钱呀,为了满足这个述求,就诞生了UTF-8。

  UTF-8是一种很别扭的编码,具体表现在他是变长的,并且兼容ASCII,ASCII字符使用1字节表示。但有得必有失,在UTF-8中,东亚的文字是用三个字节表示的,包括中文,一些不常用的字符更是用四个字节表示。于是别的国家保存的成本变高了,而美国佬却变低了。又再次坑了别人,满足了自己。但是没办法,谁叫人家是计算机界的老大呢?

 

什么是BOM

  下面是一些BOM的总结:

  BOM_UTF8 'xefxbbxbf'
  BOM_UTF16_LE 'xffxfe'
  BOM_UTF16_BE 'xfexff'

  但是,还有一种叫UTF-8无BOM模式的,这又是什么鬼。

  下用一幅图来总结:

  此时,有些人会在MBCS和UCS-2之间迷糊,大家都是两个字节表示,又有什么不同?

  MBCS是各自拓展的,有就是说很可能相同的二进制表示MBCS会出现不同的结果,而Unicode是统一拓展,保证了每种二进制表示都对应唯一一个字符,保证了唯一性,也就提高了兼容能力。

 


   

  ok,在讲完字符编码的问题之后,现在再来看一下:

   # coding:gbk 和 # coding= utf-8 之类的编码声明对python而言到底意味着什么。

  这里插播一个小技巧:

   # coding : utf-8 或者这样 # coding = utf-8 的声明方式是会报错的,这里并不是说是特点的=或者:的问题,而是空格的问题,在coding和符号之间是不能有空格的,但在符号和utf-8之类的编码名称间是运行0个或多个空格的,#和coding间也是运行0个或多个空格的。我也不知道为什么,但实际就是报错了。

#! /usr/bin/env python
#coding = utf-8
print '中文'

 

  这里coding和=号一个空格:

  报错了。

#! /usr/bin/env python
#coding= utf-8
print '中文'

  coding和=之间没空格:

  正常执行。

  不清楚是我IDE的问题还是python本来的语法是这样规定的,但其实很少有地方谈及这个地方的语法,所以这里提及一下,最后自己实验一下。

   # -*- coding: utf-8 -*- 的写法也是一样的。

 

  好了,一下进入正题:

#! /usr/bin/env python
# coding= utf-8
print '中文'
print str('中文')
print repr('中文')
print repr(u'中文')

 

  这里顺便解释一下str()函数和repr()函数在创建或转换字符串上的区别,str()得到是一个对人类可读性比较好的字符串,而print也是默认调用这个函数的。而repr()是创建或转换字符串时,得到是对机器可读性更好的字符串,就是这里得到是其编码。

  下面是另一种编码的输出:

#! /usr/bin/env python
# coding= gbk
print '中文'
print str('中文')
print repr('中文')
print repr(u'中文')

 

 

  前面两个是乱码,不过这里是我IDE的问题,我的IDE默认是用UTF-8的。

  这里改成GBK再试试。

  又可以了。

  这里再次引入一个问题,什么是文件保存编码,什么是代码运行编码。

  还是不能,那再改一下。

  这样又可以了。

 

  好,题外话多了些,但这些小的错误可能会让人纠结很久都解决不了,所以这里还是提及一下的好。

  下面进入正题,看下面的现象:

  utf-8:

  gbk:

  

  我们可以发现,随着python编码声明的不同,其字符串的编码也不同,所以我们得出一个结论:

  python中的编码声明影响的是普通字符串的编码,也是就用工程函数str()或者单纯的引号创建的出来的字符串。是随着编码声明的不同而不同的。

  此时再看一下这个现象:

  发现Unicode字符串无论在上面情况下都是一样的,因为它永远采用的是Unicode进行编码,不管你声明的到底是什么。

  Unicode字符串的创建,也就是Unicode对象的创建可以使用工厂函数unicode()或者在引号前面加个u。

  所以,我们可以得出一个在python进行编码转换的规则:

  再总体扩展一下:

  只要python支持的字符集,都能够用这样的逻辑在python的内部进行编码转换。

  只要知道了这些,那么在文件处理中就能避免许多错误了,关于python的文件处理我们下篇再讲。

  

 

3楼NoGameNoLife
写的编码发展的历史,很不错
Re: scolia
@NoGameNoLife,历史部分多是参考别人的文章,原文也写的不错,我在此基础上进行了一番总结说明并画了张示意图。
2楼bleach3
写得很不错,还详细.
1楼昵称已屏蔽
python 3已经不一样了。。,2那个u#39;#39;要多丑,有多丑
Re: scolia
@昵称已屏蔽,所以我说在python2.x是这样,已经重新在显眼的地方做出声明了。

相关内容推荐