Unicode¶
本速查表的主要目标是收集一些与 Unicode 相关的常见代码片段。在 Python 3 中,字符串由 Unicode 而不是字节表示。更多信息可以在 PEP 3100 中找到。
ASCII 码是最著名的标准,它为字符定义了数字代码。这些数值最初只定义了 128 个字符,因此 ASCII 只包含控制代码、数字、小写字母、大写字母等。然而,这不足以让我们表示世界各地存在的带重音符号的字符、汉字或表情符号。因此,开发了Unicode 来解决此问题。它定义了代码点来表示各种字符,就像 ASCII 一样,但字符数量高达 1,111,998 个。
字符串¶
在 Python 2 中,字符串以字节表示,而不是Unicode。Python 提供了不同类型的字符串,例如 Unicode 字符串、原始字符串等。在这种情况下,如果我们想声明一个 Unicode 字符串,我们为字符串字面量添加 u
前缀。
>>> s = 'Café' # byte string
>>> s
'Caf\xc3\xa9'
>>> type(s)
<type 'str'>
>>> u = u'Café' # unicode string
>>> u
u'Caf\xe9'
>>> type(u)
<type 'unicode'>
在 Python 3 中,字符串以Unicode表示。如果我们想表示一个字节字符串,我们为字符串字面量添加 b
前缀。请注意,早期版本的 Python(3.0-3.2)不支持 u
前缀。为了减轻将支持 Unicode 的应用程序从 Python 2 迁移到 Python 3 的痛苦,Python 3.3 再次支持为字符串字面量添加 u
前缀。更多信息可以在 PEP 414 中找到。
>>> s = 'Café'
>>> type(s)
<class 'str'>
>>> s
'Café'
>>> s.encode('utf-8')
b'Caf\xc3\xa9'
>>> s.encode('utf-8').decode('utf-8')
'Café'
字符¶
Python 2 将所有字符串字符视为字节。在这种情况下,字符串的长度可能与字符数不等价。例如,Café
的长度为 5,而不是 4,因为 é
被编码为 2 个字节的字符。
>>> s= 'Café'
>>> print([_c for _c in s])
['C', 'a', 'f', '\xc3', '\xa9']
>>> len(s)
5
>>> s = u'Café'
>>> print([_c for _c in s])
[u'C', u'a', u'f', u'\xe9']
>>> len(s)
4
Python 3 将所有字符串字符视为 Unicode 代码点。字符串的长度始终等价于字符数。
>>> s = 'Café'
>>> print([_c for _c in s])
['C', 'a', 'f', 'é']
>>> len(s)
4
>>> bs = bytes(s, encoding='utf-8')
>>> print(bs)
b'Caf\xc3\xa9'
>>> len(bs)
5
移植 unicode(s, ‘utf-8’)¶
在 Python 3 中移除了 unicode() 内置函数,那么如何最好地转换表达式 unicode(s, 'utf-8')
以使其在 Python 2 和 3 中都能工作?
在 Python 2 中
>>> s = 'Café'
>>> unicode(s, 'utf-8')
u'Caf\xe9'
>>> s.decode('utf-8')
u'Caf\xe9'
>>> unicode(s, 'utf-8') == s.decode('utf-8')
True
在 Python 3 中
>>> s = 'Café'
>>> s.decode('utf-8')
AttributeError: 'str' object has no attribute 'decode'
所以,真正的答案是…
Unicode 代码点¶
ord 是一个强大的内置函数,用于从给定的字符获取 Unicode 代码点。因此,如果我们想检查字符的 Unicode 代码点,我们可以使用 ord
。
>>> s = u'Café'
>>> for _c in s: print('U+%04x' % ord(_c))
...
U+0043
U+0061
U+0066
U+00e9
>>> u = '中文'
>>> for _c in u: print('U+%04x' % ord(_c))
...
U+4e2d
U+6587
编码¶
将Unicode 代码点转换为字节字符串称为编码。
>>> s = u'Café'
>>> type(s.encode('utf-8'))
<class 'bytes'>
解码¶
将字节字符串转换为Unicode 代码点称为解码。
>>> s = bytes('Café', encoding='utf-8')
>>> s.decode('utf-8')
'Café'
Unicode 规范化¶
某些字符可以用两种类似的形式表示。例如,字符 é
可以写成 e ́
(规范分解)或 é
(规范合成)。在这种情况下,即使两个字符串看起来相同,我们在比较它们时也可能会得到意外的结果。因此,我们可以规范化 Unicode 形式来解决此问题。
# python 3
>>> u1 = 'Café' # unicode string
>>> u2 = 'Cafe\u0301'
>>> u1, u2
('Café', 'Café')
>>> len(u1), len(u2)
(4, 5)
>>> u1 == u2
False
>>> u1.encode('utf-8') # get u1 byte string
b'Caf\xc3\xa9'
>>> u2.encode('utf-8') # get u2 byte string
b'Cafe\xcc\x81'
>>> from unicodedata import normalize
>>> s1 = normalize('NFC', u1) # get u1 NFC format
>>> s2 = normalize('NFC', u2) # get u2 NFC format
>>> s1 == s2
True
>>> s1.encode('utf-8'), s2.encode('utf-8')
(b'Caf\xc3\xa9', b'Caf\xc3\xa9')
>>> s1 = normalize('NFD', u1) # get u1 NFD format
>>> s2 = normalize('NFD', u2) # get u2 NFD format
>>> s1, s2
('Café', 'Café')
>>> s1 == s2
True
>>> s1.encode('utf-8'), s2.encode('utf-8')
(b'Cafe\xcc\x81', b'Cafe\xcc\x81')
避免 UnicodeDecodeError
¶
当字节字符串无法解码为 Unicode 代码点时,Python 会引发 UnicodeDecodeError。如果我们想避免此异常,我们可以将replace、backslashreplace 或ignore 传递给 decode 中的 errors 参数。
>>> u = b"\xff"
>>> u.decode('utf-8', 'strict')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
>>> # use U+FFFD, REPLACEMENT CHARACTER
>>> u.decode('utf-8', "replace")
'\ufffd'
>>> # inserts a \xNN escape sequence
>>> u.decode('utf-8', "backslashreplace")
'\\xff'
>>> # leave the character out of the Unicode result
>>> u.decode('utf-8', "ignore")
''
长字符串¶
以下代码片段显示了在 Python 中声明多行字符串的常用方法。
# original long string
s = 'This is a very very very long python string'
# Single quote with an escaping backslash
s = "This is a very very very " \
"long python string"
# Using brackets
s = (
"This is a very very very "
"long python string"
)
# Using ``+``
s = (
"This is a very very very " +
"long python string"
)
# Using triple-quote with an escaping backslash
s = '''This is a very very very \
long python string'''