字符串——Unicode 字符串
Python的字符串还带有在国际化字符集中处理文本所需的完整Unicode支持。比如,日语和俄语的字母表中的字符就在ASCII集之外。这些非ASCII文本可以显示在网页,电子邮件,图形用户界面,JSON,XML或其他地方。当它出现时,处理好它就需要Unicode支持。Python内置了这些支持,但不同Python产品线的Unicode支持的形式也不同,并且这是他们最明显的差异之一。
在Python 3.X中,普通的 str
字符串处理Unicode文本(包括ASCII,它只是一种简单的Unicode);明显不同的 bytes
字符串类型表示原始字节值(包括媒体和编码过的文本);并且为了兼容2.X,2.X的Unicode字面量在3.3和之后版本中被支持(它们和普通的3.X str
字符串处理方式相同):
>>> 'sp\xc4m' # 3.X: 普通 `str` 字符串是Unicode 文本
'spÄm'
>>> b'a\x01c' # 字节字符串是基于字节的数据
b'a\x01c'
>>> u'sp\u00c4m' # 2.X Unicode 字面量适用于3.3+:只是字符串
'spÄm'
在Python 2.X中,普通的 str
字符串处理8位字符串(包括ASCII文本)和原始字节值;明显不同的 unicode
字符串类型表示Unicode文本;并且为了兼容3.X,3.X的字节字面量在2.6和之后的版本中被支持(它们和普通2.X str
字符串处理方式相同):
>>> print u'sp\xc4m' # 2.X: Unicode 字符串是一个不同的类型
spÄm
>>> 'a\x01c' # 普通的 `str` 字符串包含基于字节的文本/数据
'a\x01c'
>>> b'a\x01c' # 3.X 字节字面量在2.6+中可用:只是字符串
'a\x01c'
规范的说,在2.X和3.X中,非Unicode字符串都是8位字节的序列,在可能的情况下使用ASCII字符打印,而Unicode字符串是Unicode 码点(字符的识别数字,当编码到文件或存储到内存中时不一定映射到单字节)的序列。事实上,字节的概念不适用于Unicode:一些编码包括对一个字节太大的字符码点,而且在一些编码和内存存储方案下,甚至简单的7位ASCII文本也不是每个字符串存储一个字节:
>>> 'spam' # 在内存中,字符可以为1,2或4个字节
'spam'
>>> 'spam'.encode('utf8') # 在文件中,以UTF-8编码为4个字节
b'spam'
>>> 'spam'.encode('utf16') # 但在UTF-16中编码为10个字节
b'\xff\xfes\x00p\x00a\x00m\x00'
3.X 和 2.X 都支持我们之前见过的 bytearray
字符串类型,它本质上是一个bytes
字符串(在 2.X中是 str
),支持列表对象的大多数就地可变更改操作。
3.X和2.X也都支持使用 \x
十六进制和短 \u
和长\U
Unicode转义,还有在程序源文件中声明的文件级编码。下面是在3.X中非ASCII字符编码的三种方式(在2.X中添加一个前导“u”并说“print”效果相同):
>>> 'sp\xc4\u00c4\U000000c4m' # 3.X
'spÄÄÄm'
>>> print u'sp\xc4\u00c4\U000000c4m' # 2.X
spÄÄÄm
这些值的含义和它们的使用方式在文本字符串(3.X中是普通字符串,而2.X中是Unicode)和字节字符串(3.X中是字节,而2.X中是普通字符串)之间是不同的。所有这些转义都能被用来嵌套真正的Unicode码点序列值整数到文本字符串中。作为对比,字节字符串只使用 \x
十六进制转义来嵌套文本的编码形式,而不是它的解码的码点值——只有对某些编码和字符,编码字节和码点是相同的:
>>> '\u00A3', '\u00A3'.encode('latin1'), b'\xA3'.decode('latin1')
('£', b'\xa3', '£')
作为一个显著的区别,只要普通字符串中的字符全是ASCII,Python 2.X 允许它的普通字符串和Unicode字符串在表达式中混合;作为对比,Python 3.X有一个更严格的模型,它从不允许它的普通字符串和字节字符串未经明确的转换而混合:
u'x' + b'y' # 在 2.X 中可行(其中 b 可选且被忽略)
u'x' + 'y' # 在2.X 中可行: u'xy'
u'x' + b'y' # 在 3.3 中出错(其中 u 可选且被忽略)
u'x' + 'y' # 在 3.3 中可行: 'xy'
'x' + b'y'.decode() # 如果解码字节为字符串:'str',则在3.X中可行
'x'.encode() + b'y' # 如果编码字符串为字节,则在3.X中可行:b'xy'
除了这些字符串类型,Unicode处理主要集中在文件中来回传输文本数据——当存储到文件时,文本是被编码为字节,当读取回内存时是被解码为字符(也就是码点)。一旦文本被加载到内存中,通常只将它作为解码形式的字符串来处理。
但是,由于这个模型,在3.X中文件也是特定于内容的:文本文件实现指定名称的编码和接收和返回 str
字符串,但二进制文件对于原始二进制数据用 bytes
字符串来处理。在Python 2.X中,普通文件的内容是 str
字节,一个特殊的 codecs
模块处理Unicode和使用 unicode
类型表示内容。
在本章稍后将再次见到文件中的Unicode,但将Unicode故事的剩余部分留在本书后面。它在第25章的一个与货币符号相关的例子中简短地出现,但大部分内容被推迟到本书的高级主题部分。Unicode在一些领域十分重要,但许多程序员只是有基本的了解也能勉强应付过去。如果数据全是ASCII文本,字符串和文件的主题在2.X和3.X中基本都是相同的。并且如果是编程新手,可以安全地推迟大多数Unicode细节直到已经精通了字符串基础。