2.9 将Unicode文本标准化¶

问题¶

你正在处�Unicode字符串,需�确�所有字符串在底层有相�的表示。

解决方案¶

在Unicode中,�些字符能够用多个�法的编�表示。为了说明,考虑下�的这个例�:

>>> s1 = 'Spicy Jalape\u00f1o'
>>> s2 = 'Spicy Jalapen\u0303o'
>>> s1
'Spicy Jalapeño'
>>> s2
'Spicy Jalapeño'
>>> s1 == s2
False
>>> len(s1)
14
>>> len(s2)
15
>>>

这里的文本�Spicy Jalapeño�使用了两�形��表示。 第一�使用整体字符�ñ�(U+00F1),第二�使用拉�字��n���跟一个�~�的组�字符(U+0303)。

在需�比较字符串的程�中使用字符的多�表示会产生问题。 为了修正这个问题,你�以使用unicodedata模�先将文本标准化:

>>> import unicodedata
>>> t1 = unicodedata.normalize('NFC', s1)
>>> t2 = unicodedata.normalize('NFC', s2)
>>> t1 == t2
True
>>> print(ascii(t1))
'Spicy Jalape\xf1o'
>>> t3 = unicodedata.normalize('NFD', s1)
>>> t4 = unicodedata.normalize('NFD', s2)
>>> t3 == t4
True
>>> print(ascii(t3))
'Spicy Jalapen\u0303o'
>>>

normalize() 第一个�数指定字符串标准化的方�。 NFC表示字符应该是整体组�(比如�能的�就使用�一编�),而NFD表示字符应该分解为多个组�字符表示。

Python�样支�扩展的标准化形�NFKC和NFKD,它们在处��些字符的时候增加了�外的兼容特性。比如:

>>> s = '\ufb01' # A single character
>>> s
'�'
>>> unicodedata.normalize('NFD', s)
'�'
# Notice how the combined letters are broken apart here
>>> unicodedata.normalize('NFKD', s)
'fi'
>>> unicodedata.normalize('NFKC', s)
'fi'
>>>

讨论¶

标准化对于任何需�以一致的方�处�Unicode文本的程�都是�常��的。 当处��自用户输入的字符串而你很难去控制编�的时候尤其如此。

在清�和过滤文本的时候字符的标准化也是很��的。 比如,�设你想清除掉一些文本上�的�音符的时候(�能是为了�索和匹�):

>>> t1 = unicodedata.normalize('NFD', s1)
>>> ''.join(c for c in t1 if not unicodedata.combining(c))
'Spicy Jalapeno'
>>>

最�一个例�展示了 unicodedata 模�的�一个��方�,也就是测试字符类的工具函数。 combining() 函数�以测试一个字符是�为和音字符。 在这个模�中还有其他函数用于查找字符类别,测试是�为数字字符等等。

Unicode显然是一个很大的主题。如果想更深入的了解关于标准化方�的信�, 请看考 Unicode官网中关于这部分的说明 Ned Batchelder在 他的网站 上对Python的Unicode处�问题也有一个很好的介�。