Unicode
世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。
可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是Unicode,就像它的名字都表示的,这是一种所有符号的编码。
Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字严。
Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
UTF-8
UTF-8是最流行的一种对Unicode进行传播和存储的编码方式。它用不同的bytes来表示每一个代码点。ASCII字符每个只需要用一个byte,与ASCII的编码是一样的。所以说ASCII是UTF-8 的一个子集。
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。每个方法中都有一个参数来表明你要操作的编码类型。
当编码或者解码的时候,你可以指明如果encode或decode不能够处理数据的时候,会发生什么情况。encode或者decode的第二个参数指明了规则。默认的值是“strict” ,意味着解码或者编码一个不正确的值会抛出一个异常。“replace”值意味着,失败时将会返回一个标准的替代字符。当编码的时候,替代值是一个问号,所以任何不能被编码的值将会产生一个“?”。“ignore”会直接将不能解码的bytes丢掉。
Python3
跟Python2类似,Python3也有两种类型,一个是Unicode,一个是byte码。但是它们有不同的命名。
现在你从普通文本转换成“str”类型后存储的是一个unicode,“bytes”类型存储的是byte串。你也可以通过一个b前缀来制造byte串。
所以在Python2中的“str”现在叫做“bytes”,而 Python2中的“unicode”现在叫做“str”。这比起Python2中更容易理解,因为Unicode是你总想要存储的内容。而bytes字符串只有你在想要处理byte的时候得到。
读取文件
Python对于读取文件有两种方式,一种是二进制,一种是文本。在Python2中,它只会影响到行尾符号,甚至在Unix系统上的时候,基本没有区别。
在Python3中,这两种模式将会返回不同的结果。当你用文本模式打开一个文件时不管你是用的“r”模式或者默认的模式,读取成功的文件将会自动转码成unicode,你会得到str对象。
如果你用二进制模式打开一个文件,在参数中输入“rb”,那么从文件中读取的数据会是bytes,对它们没有任何处理。
隐式的对bytes到unicode的处理使用的是locale.getpreferedencoding(),然而它有可能输出你不想要的结果。比如,当你读取hi_utf8.txt时,他被解码成语言偏好中所设置的编码方式,如果我们这些例子在Windows中创建的话,那么就是“cp1252”。像ISO 8859-1, CP-1252这些可以得到任意的byte值,所以不会抛出UnicodeDecodeError,当然也意味着它们会直接将数据解码成CP-1252,制造出我们并不需要的垃圾信息。为了文件读取正确的话,你应该指明想要的编码。open函数现在已经可以通过参数来指明编码。