字符编码
事实之一:计算机中的一切均为 bytes(字节)。硬盘中的文件为一系列的 byte 组成,网络中传输的只有 byte。所有的信息,在你写的程序中进进出出的,均由 byte 组成。
为了表示各种文字,我们有大约 50 年的时间都在用 ASCII 码。每一个 byte 被赋予 95 种符号的一种,所以,当我给你发送 byte 值为 65 的时候,你知道我想表达一个大写的 A。
ISO Latin 1,或者 8859-1 对 ASCII 的 96 种字符进行了扩展。这也许是你用一个 byte 可以做的最多的事情了。因为 byte 中没有容量可以存储更多的符号了。(8bit)
事实之二是,世界上的字符远远比256个要多。一个简单的byte不能够表达世界范围内的字符。在你玩”编码打地鼠”的时候,你多么的希望世界上所有的人都说英语,但是事实并不是这样,人们需要更多的符号来交流。
事实一和二共同造成了计算机设备结构与世界人类需求的一个冲突。
人们开始创造两个 byte 的字符集,但是仍然像碎片一样,只能够服务于不同地域的一部分人。
当时产生了不同的标准,讽刺的是,他们都不足以满足所有的符号的需求。
Unicode 就是为了解决之前的老的字符集问题。Unicode 分配整形,被成为代码点( UNICODE 的字符被成为代码点( CODE POINTS )用 U 后面加上 XXXX 来表现,其中, X 为16进制的字符)来表示字符。它有 110 万的代码点,其中有十一万被占用,所以它可以有很多很多的空间可供未来的增长使用。
所以说 Unicode 提供了所有我们需要的字符的空间。但是我们仍然需要处理事实一中所碰到的问题:计算机只能看懂 bytes 。我们需要一种用 bytes 来表示 Unicode 的方法这样才可以存储和传播他们
Unicode 标准定义了多种方法来用 bytes 来表示成代码点,被成为 encoding 。
UTF-8 是最流行的一种对 Unicode 进行传播和存储的编码方式。它用不同的 bytes 来表示每一个代码点。ASCII 字符每个只需要用一个 byte ,与 ASCII 的编码是一样的。所以说 ASCII 是 UTF-8 的一个子集。
这里我们展现了几个怪异字符的 UTF8 的表示方法。 ASCII 字符 H 和 I 只用一个 byte 就可以表示。其他的根据代码点的不同使用了两个或者三个 bytes 。尽管有些并不常用,但是一些代码点使用到四个 bytes。
Python2
在 Python2 中,有两种字符串数据类型。一种纯旧式的文字: “str” 对象,存储 bytes 。如果你使用一个 “u” 前缀,那么你会有一个 “unicode” 对象,存储的是 code points 。在一个 unicode 字符串中,你可以使用反斜杠 u(u) 来插入任何的 unicode 代码点。
你可以注意到 “string” 这个词是有问题的。不管是 “str” 还是 “unicode” 都是一种 “string” ,这会吸引叫它们都是 string ,但是为了直接还是将它们明确区分来。
如果想要在 unicode 和 bytes 间转换的话,两者都有一个方法。 Unicode 字符串会有一个 .encode 方法来产生 bytes , bytes 串会有一个 .decode 方法来产生 unicode 。每个方法中都有一个参数来表明你要操作的编码类型。
我们可以定义一个 Unicode 字符串叫做 my_unicode ,然后看这九个字符,我们使用 encode 方法来创建 my_unicode 的 bytes 串。会有 19 个 bytes ,想你所期待的那样。将 bytes 串来 decode 将会得到 utf-8 串。
Python 2 的哲学就是 Unicode 字符串和 byte 字符串是可以混合的,它试图去通过自动转换来减轻你的负担。就像在 int 和 float 之间的转换一样, int 到 float 的转换不会失败,byte 字符串到 unicode 字符串会失败。
Python 2 已经试图在处理 unicode 和 byte 串的时候变得有用些。如果你系那个要把 Unicode 字符串串和 byte 字符串来组合起来的话, Python 2 将会自动的将 byte 串来解码成 unicode 字符串。从而产生一个新的 Unicode 字符串。
比如,我们想要连接 Unicode 串 “hello” 和一个 byte 字符串 “world”。结果是一个 Unicode 的 “hello world”。在我们看来。Python 2 将 “world” 使用 ASCII codec 进行了解码。这次在解码中使用的字符集的值与 sys.getdefaultencoding() 的值相等。
Python 2 悄悄掩盖了 byte 到 unicode 的转换,让程序在处理 ASCII 的时候更加简单。你付出的代价就是在处理非 ASCII 的时候将会失败。
最重要的事实之三:byte 和 unicode 都非常重要,你必须将两个都处理好。你不能假设所有的字符串都是 byte,或者所有的字符串都是 unicode,你必须适当地运用它们,必要时转换它们。
Python3
跟 Python 2 类似,Python 3 也有两种类型,一个是 Unicode,一个是 byte 码。但是它们有不同的命名。
现在你从普通文本转换成 “str” 类型后存储的是一个 unicode, “bytes” 类型存储的是 byte 串。你也可以通过一个 b 前缀来制造 byte 串。
所以在 Python 2 中的 “str” 现在叫做 “bytes”,而 Python 2 中的 “unicode” 现在叫做 “str”。这比起Python 2中更容易理解,因为 Unicode 是你总想要存储的内容。而 bytes 字符串只有你在想要处理 byte 的时候得到。
另外如果一个 Unicode 字符串和 byte 字符串中包含的是相同的 ASCII 码,Python 2 中将认为两个是相等的,而在 Python 3 中不会。这样做的结果是 Unicode 中的键不能找到 byte 字符串中的值,反之亦然,然而在 Python 2 中是可行的。
正如我们在事实一中所看到的,在你的程序中进进出出的只有 bytes, 但是在你的程序中你不必处理所有的 bytes。最好的策略是将输入的 bytes 马上解码成 unicode。你在程序中均使用 unicode ,当在进行输出的时候,尽早将之编码成 bytes 。
制造一个 Unicode 三明治, bytes 在外, Unicode 在内。
第二条规则是:你需要知道你现在处理的是哪种类型的数据,在你的程序中任何一个位置,你需要知道你处理的是 byte 串还是一个 unicode 串。它不能是一种猜测,而应该被设计好。
另外,如果你有一个 byte 串的话,如果你想对它进行处理。那么你应该知道它是怎样的编码。
在对你的代码进行 debug 的时候,不能仅仅将之打印出来来看它的类型。你应该查看它的 type ,或者查看它 repr 之后的值来查看你的数据到底是什么类型。