编码问题的发展及python2和python3的编码差异

人类使用文本,计算机使用字节序列。

                                                                                                                                                        ——Esther Nam 和 Travis Fischer

                                                                                                                                        “Character Encoding and Unicode in Python”

前奏:编码与解码

编码和解码的概念:把码位转换成字节序列的过程是编码;把字节序列转换成码位的过程是解码。

  1. >>> str_test = "今天天气好蜣螂,处处好风光。"

  2. >>> rst = str_test.encode('utf8')  # 编码过程,让计算机明白

  3. >>> rst

  4. b'\xe4\xbb\x8a\xe5\xa4\xa9\xe5\xa4\xa9\xe6\xb0\x94\xe5\xa5\xbd\xe8\x9c\xa3\xe8\x9e\x82\xef\xbc\x8c\xe5\xa4\x84\xe5\xa4\x84\xe5\xa5\xbd\xe9\xa3\x8e\xe5\x85\x89\xe3\x80\x82'

  5. >>> rst.decode('utf8')  # 解码过程,让人类明白

  6. '今天天气好蜣螂,处处好风光。'

天启:黎明之前

标准:字符集

双方要想实现通信,需要先商量一下,比如两个同学考试前商量着传答案,可以约定,伸一个手指头代表选择A,眨眨眼代表选择B….

有人可能会问,为啥不是伸两个指头代表选择B?这也是会出现编码问题的一个重要原因,约定不同!(考试之前有一套行之有效的统一的约定是多么的至关重要)。

同样,回到计算机的编码问题上,人给计算机传答案,得和计算机事先约定好一套行之有效的《逢考必过宝典》。常用的宝典及其发展历史大致如下。

ASCII(erican Standard Code for Information Interchange)编码,不厚道的美国人

用7bit进行编码,因为美国使用英文,字符比较少,26个英文字母,算上大小写才52个,加上其他的一些标点和控制键,128种状态足够了,压根没打算和中日韩这些学生传答案。

iso8859_1:  拉丁语系国家

拉丁语系国家,美国大哥仗着自己英语好,不打算带着自己玩了,那好吧,自己也建个群,搞了一套《阿拉伯逢考必过手册》。拉丁语系国家感觉自己也飘起来了,比老美也好不到哪里,用了8bit编码。最多也就256种状态。也就是最多存储256个符号。而汉字少数也得几万,常用的也得几千吧,根本无法满足中国同学作文与阅读理解考试时候的刚需!

GBK和GB2312:汉字编码

于是,聪明的中国同学也开始开发自己的《中华助考神奇,三年模拟五年实战》。中国国家比较大,拥有56个民族,为了方便各民族,GB代表是国家标准的意思——国标。与此同时邻座的日韩也搞了自己的《宝典》。

GBK与GB2312的区别:https://zhidao.baidu.com/question/40269499.html

Unicode(utf-8): 万国码

考试座位通常是随机分配的,比如考数学的时美美同学与数学比较好的中中同学邻座,可是这两位同学练得分别是两门相克的武林绝学,根本没办法统一起来。于是经过几场考试后,大家为了应对残酷无情的监考老师,决定开发一套通用的《地球考试手册》就是Unicode字符集(编码)。这下可算让大家松了一口老气!

矛盾:各为其主

第二天考场上,韩韩同学(韩国)和美美同学(美国)在邻座,两个人商量好要一块把《马

克思原理》拿到全班最高分,美美遇到了一个不确定的答案,自己给韩韩同学发信号,但是他感觉《地球考试手册》太复杂,没有自己的《宝典》方便。于是就用自己的《宝典》向韩韩求助,可想而知,韩韩同学看不明白,转而用自己的宝典和自己的同班同学去交流。留下美美一个人饮酒醉。

中中与俄俄同学就比较聪明,他们一样用各自熟系的《宝典》,但是带了个长江七号点读机,长江七号先把《中华助考神奇,三年模拟五年实战》转换成《地球考试手册》然后再转换

《俄俄宝典三千问》。同样,当俄俄同学给中中同学发信号时,这个过程逆过来。最后结果可想而知,中中同学和俄俄同学取得了不错的成绩。

战场:少年小P(python)的成长烦恼

同样的道理,当python要做编码转换的时候,会借助于内部的编码,转换过程是这样的:
                                               原有编码 -> 内部编码 -> 目的编码

Python2默认的是ASCII编码、需要通过Unicode编码实现不同编码的额转换

注意:unicode和utf-8之间不需要转换,可以直接互相打印,GBK如果需要和iso8859_1编码要通unicode编码转换。

UTF-8编码:是对Unicode编码的压缩和优化,他不再使用最少使用2个字节,而是将所有的字符和符号进行分类:ascii码中的内容用1个字节保存、欧洲的字符用2个字节保存,东亚的字符用3个字节保存...

每次使用py2进行编码的时候,要在文件头添加# encoding:utf-8,非常烦恼。

高潮:小P的不惑之年——py3

Python 3 明确区分了人类可读的文本字符串和原始的字节序列。隐式地把字节序列转换成 Unicode 文本已成过去。

“字符串”是个相当简单的概念:一个字符串是一个字符序列。问题出在“字符”的定义上。在 2015 年,“字符”的最佳定义是 Unicode 字符。因此,从 Python 3 str 对象中获取的元素是 Unicode 字符,这相当于从 Python 2 unicode 对象中获取的元素,而不是从 Python 2 str 对象中获取的原始字节序列。

现在,Python 3 的源码不再限于使用 ASCII,而是默认使用优秀的 UTF-8 编码,因此要修正源码的陈旧编码(如 'cp1252')问题,最好将其转换成 UTF-8,别去麻烦 coding 注释。如果你用的编辑器不支持 UTF-8,那么是时候换一个了

落幕:英雄的无奈

          图:编码问题及解决方案

多数非 UTF 编解码器只能处理 Unicode 字符的一小部分子集。把文本转换成字节序列时,如果目标编码中没有定义某个字符,那就会抛出UnicodeEncodeError 异常,除非把 errors 参数传给编码方法或函数,对错误进行特殊处理。

 

不是每一个字节都包含有效的 ASCII 字符,也不是每一个字符序列都是有效的 UTF-8 或 UTF-16。因此,把二进制序列转换成文本时,如果假设是这两个编码中的一个,遇到无法转换的字节序列时会抛出UnicodeDecodeError。

 

使用预期之外的编码加载模块时抛出的SyntaxError

Python 3 默认使用 UTF-8 编码源码,Python 2(从 2.5 开始)则默认使用ASCII。如果加载的 .py 模块中包含 UTF-8 之外的数据,而且没有声明编码,会得到类似下面的消息:

SyntaxError: Non-UTF-8 code starting with '\xe1' in file ola.py on line1, but no encoding declared; see http://python.org/dev/peps/pep-0263/for details

图展示了不同编解码器对“A”和高音谱号等字符编码后得到的字节序列。注意,后 3 种是可变长度的多字节编码。

图: 12个字符,它们的码位及不同编码的字节表述(十六进制,星号表明该编码不支持表示该字符)

图中的星号表明,某些编码(如 ASCII 和多字节的 GB2312)不能表示所有 Unicode 字符。然而,UTF 编码的设计目的就是处理每一个Unicode 码位。

图中展示的是一些典型编码,介绍如下。

latin1(即 iso8859_1)

  一种重要的编码,是其他编码的基础,例如 cp1252 和Unicode(注意,latin1 与 cp1252 的字节值是一样的,甚至连码位也相同)。

cp1252

  Microsoft 制定的 latin1 超集,添加了有用的符号,例如弯引号和€(欧元);有些 Windows 应用把它称为“ANSI”,但它并不是 ANSI 标准。

cp437

  IBM PC 最初的字符集,包含框图符号。与后来出现的 latin1 不兼容。

gb2312

  用于编码简体中文的陈旧标准;这是亚洲语言中使用较广泛的多字节编码之一。

utf-8

  目前 Web 中最常见的 8 位编码; 与 ASCII 兼容(纯 ASCII 文本是有效的 UTF-8 文本)。

小端存储、大端存储https://www.cnblogs.com/wood2012/p/8540169.html

在小字节序设备中,各个码位的最低有效字节在前面。

在大字节序 CPU 中,编码顺序是相反的

UTF-16 有两个变种:UTF-16LE,显式指明使用小字节序;UTF-16BE,显式指明使用大字节序。如果使用这两个变种,不会生成 BOM(一种在utf_16的隐式标志、UTF-16 编码的序列开头有几个额外的字节)

  1. >>> u16le = 'El Niño'.encode('utf_16le')
  2. >>> list(u16le)

  3. [69, 0, 108, 0, 32, 0, 78, 0, 105, 0, 241, 0, 111, 0]

  4. >>> u16be = 'El Niño'.encode('utf_16be')

  5. >>> list(u16be)

  6. [0, 69, 0, 108, 0, 32, 0, 78, 0, 105, 0, 241, 0, 111]

如果有 BOM,UTF-16 编解码器会将其过滤掉,为你提供没有前导ZERO WIDTH NO-BREAK SPACE 字符的真正文本。根据标准,如果文件使用 UTF-16 编码,而且没有 BOM,那么应该假定它使用的是 UTF-16BE(大字节序)编码。然而,Intel x86 架构用的是小字节序,因此有很多文件用的是不带 BOM 的小字节序 UTF-16 编码。

与字节序有关的问题只对一个字(word)占多个字节的编码(如 UTF-16 和 UTF-32)有影响。UTF-8 的一大优势是,不管设备使用哪种字节序,生成的字节序列始终一致,因此不需要 BOM。尽管如此,某些Windows 应用(尤其是 Notepad)依然会在 UTF-8 编码的文件中添加BOM;而且,Excel 会根据有没有 BOM 确定文件是不是 UTF-8 编码,否则,它假设内容使用 Windows 代码页(codepage)编码。UTF-8 编码的 U+FEFF 字符是一个三字节序列:b'\xef\xbb\xbf'。因此,如果文

件以这三个字节开头,有可能是带有 BOM 的 UTF-8 文件。然而,Python 不会因为文件以 b'\xef\xbb\xbf' 开头就自动假定它是 UTF-8编码的。

参考资料:《流畅的Python》 Luciano Ramalho 人民邮电出版社 安道、吴珂译

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值