概述
最近工作中的项目同时使用到了 Python2 和 Python3 ,遇到了文本和字节方面的 tricks,自己之前对这方面不太了解,学习并总结一下。
编码介绍
Unicode 标准
Unicode 是用于表示文本以供计算机进行处理的通用字符编码标准。Unicode 标准提供了一种对多语种纯文本进行一致编码的方法,便于国际文本文件的交换。
字符 的最佳定义是 Unicode 字符 。Unicode 只是一个符号集,它只规定了符号的二进制代码,并没有规定这个二进制代码应该如何存储。
UTF-8
UTF-8 字符编码是 Unicode 的实现方式之一。
UTF-8 是一种变长的编码方式,它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
Python 中的文本和字节
Python3 中从 str 对象中获取的元素就是 Unicode 字符,可以通过 编码 (encode) 将 文本 转化为 字节。
然而 Python2 中从 str 对象中获取的元素是字节序列,只有通过 解码 (decode) 才能将 字节 转化为 文本 。
下面使用两个版本的 Python 对字符串进行操作以作解释:
# py3
>>> s = '123'
>>> type(s)
# 文本
>>> b = s.encode('utf-8')
>>> b
b'123'
>>> type(b)
# 字节序列
# py2
>>> s = '123'
>>> type(s)
# 这里是字节序列
>>> b = s.encode('utf-8')
>>> b
'123'
>>> type(b)
# 这里还是字节序列
>>> b.decode() # decode 出来的才是文本
u'123'
>>> type(b.decode())
# 文本
>>> c = '一二三'
>>> c
'\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89' # 字节序列
从上面的两个代码片段可以总结出 Python2 和 Python3 对于字符串处理上的区别:
Python 中的 u, b, r
Python 的字符串有时候前面会加一个 u ,r 或者 b ,其含义如下:
u :表示字符串中的元素是 Unicode 字符。结合上面表格的结论,可以认为:在 Python3 中,字符串前面是否加 u 的效果是一致的。在 Python2 中,字符串前面加 u 表示其中的元素是 Unicode 字符,不加 u 表示 bytes。
b :表示字符串中的元素是 Bytes。同结合上面表格的结论,可以认为:在 Python2 中,字符串前面是否加 b 的效果是一致的。在 Python3 中,字符串前面加 b 表示其中的元素是 bytes,不加 b 表示 Unicode 字符。
r :表示字符串是 原始字符串(raw string) ,里面的字符都是 raw string literals ,与 Unicode 和 Bytes 无关,因此 Python2 和 Python3 中含义是一致的。它的作用是使解释器不会对诸如 \n, \t 等转义字符进行转义:
# py3
>>> s1 = '123\n123\t123'
>>> s1
'123\n123\t123'
>>> s2 = r'123\n123\t123'
>>> s2
'123\\n123\\t123' # 不转义
Python2 和 Python3 在字符串处理方面的兼容
既然 Python2 和 Python3 在字符串的处理方面有所不同,但是实际工作中却需要写出兼容两种版本的代码,那么应该如何处理呢?
我的做法是使用 __future__ 模块:
from __future__ import unicode_literals
该模块的作用是将 Python2 的字符串字面量的类型变为文本,而不是字节,因此与 Python3 是一样的。
举个栗子:
# py2
>>> from __future__ import unicode_literals
>>> s = '123'
>>> s
u'123'
>>> type(s)
# 文本
>>> b = b'123'
>>> b
'123'
>>> type(b)
# 字节
总结
目前 Python2 与 Python3 是并存的,因此在编写代码过程中需要注意其中的差异和兼容性,不然就要出锅了hhh,别问我为什么这么说 QuQ(虽然 Python2 在 2020 年 1 月就要停止维护了)。