19.python的编码问题,,  在正式说明之前,


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

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

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

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

什么是BOM

  当一个文本编辑要打开一个文件时,它表示懵逼了。世间编码如此之多,我究竟要用什么哪种编码去解码呀?你总得告诉我吧!

  此时,UTF就进入了BOM来表示编码。所谓的BOM就是文件使用编码的标识符,就和python的编码声明一样,告诉文本编辑器我用的是什么编码,下面的你都用那个编码去解码就行。

  同样的,只有文本编辑器在文件开头的地方读到了关于BOM的描述,就能够进行正确的界面了。

  下面是一些BOM的总结:

  BOM_UTF8 ‘\xef\xbb\xbf‘
  BOM_UTF16_LE ‘\xff\xfe‘
  BOM_UTF16_BE ‘\xfe\xff‘

  同样了,为了我们自己编辑的文件的编码也能被正确识别,我们也要写入BOM,一般由编辑器完成。但不写也可以,只有在打开文件的时候自己手动选择用什么去解码也是可以的。

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

  因为UTF-8实在太流行了,所以文本编辑器默认会首先用UTF-8进行解码。即使是保存时默认使用ANSI(MBCS)的记事本,在读取文件时也是先使用UTF-8测试编码,如果可以成功解码,则使用UTF-8解码。记事本这个别扭的做法造成了一个BUG:如果你新建文本文件并输入"姹塧"然后使用ANSI(MBCS)保存,再打开就会变成"汉a"。)

  下用一幅图来总结:

技术分享

  此时,有些人会在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-8print ‘中文‘

  这里coding和=号一个空格:

技术分享

  报错了。

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

  coding和=之间没空格:

技术分享

  正常执行。

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

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

  好了,一下进入正题:

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

技术分享

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

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

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

技术分享 

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

技术分享

  这里改成GBK再试试。

技术分享

技术分享

  又可以了。

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

技术分享

技术分享

  还是不能,那再改一下。

技术分享

技术分享

  这样又可以了。

  其实保存编码,也就是IDE Encoding设置的是在磁盘中保存和打开的时候的编码。假设我使用的是windows的文本编辑器写的代码,也就是用ANSI保存的,如何我再用其他编码打开就会出现乱码,这无关运行的事。而运行编码是则是影响python交互界面的编码,我在前面的python的第一个程序中也说过这个问题,我用windows的cmd运行一个python文件,为什么我在python中声明了可以使用中文的utf-8,却在输出显示的时候还是出现了乱码?这是因为虽然python输出的utf-8的编码,但是cmd这个家伙默认是用GBK去解码的,所以出现了显示上的乱码。但这并不是真正的乱码,换用一个支持utf-8的显示环境就可以了。

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

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

  utf-8:

技术分享

  gbk:

  技术分享

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

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

  此时再看一下这个现象:

技术分享

技术分享

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

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

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

技术分享

  再总体扩展一下:

技术分享

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

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

  

19.python的编码问题

相关内容

    暂无相关文章

评论关闭