规范化Unicode字符串


因为Unicode有组合字符(变音符号和附加到前一个字符上的记号,打印时作为一个整
体),所以字符串比较起来很复杂。

#café”这个词可以使用两种方式构成,分别有4个和5个码位,但是结果完全一样:
>>> s1 = 'café' 
>>> s2 = 'cafe\u0301' 
>>> s1, s2 
('café', 'café') 
>>> len(s1), len(s2) 
(4, 5) 
>>> s1 == s2 
False

分析:U+0301是COMBINING ACUTE ACCENT,加在“e”后面得到“é”。在Unicode标准中,'é’和’e\u0301’这样的序列叫“标准等价物”(canonical equivalent),应用程序应该把它们视作相同的字符。但是,Python看到的是不同的码位序列,因此判定二者不相等。

NFC(Normalization Form C)使用最少的码位构成等价的字符串;
NFD把组合字符分解成基字符和单独的组合字符;
这两种规范化方式都能让比较行为符合预期:

>>> from unicodedata import normalize 
>>> s1 = 'café'  # 把"e"和重音符组合在一起 
>>> s2 = 'cafe\u0301'  # 分解成"e"和重音符 
>>> len(s1), len(s2) 
(4, 5) 
>>> len(normalize('NFC', s1)), len(normalize('NFC', s2)) 
(4, 4) 
>>> len(normalize('NFD', s1)), len(normalize('NFD', s2)) 
(5, 5) 
>>> normalize('NFC', s1) == normalize('NFC', s2) 
True 
>>> normalize('NFD', s1) == normalize('NFD', s2) 
True

在另外两个规范化形式(NFKC和NFKD)的首字母缩略词中,字母K表示“compatibility”
(兼容性)。这两种是较严格的规范化形式,对“兼容字符”有影响。

大小写折叠

大小写折叠其实就是把所有文本变成小写,再做些其他转换。这个功能由str.casefold()方法(Python 3.3新增)支持。

>>> micro = 'µ' 
>>> name(micro) 
'MICRO SIGN' 
>>> micro_cf = micro.casefold() 
>>> name(micro_cf) 
'GREEK SMALL LETTER MU' 
>>> micro, micro_cf 
('µ', 'μ') 
>>> eszett = 'ß' 
>>> name(eszett) 
'LATIN SMALL LETTER SHARP S' 
>>> eszett_cf = eszett.casefold() 
>>> eszett, eszett_cf 
('ß', 'ss')

分析:对于只包含latin1字符的字符串s,s.casefold()得到的结果与s.lower()一样,除了两个特殊的字符:微符号’µ’会变成小写的希腊字母“μ”(在多数字体中二者看起来一样);德语 Eszett(“sharp s”,ß)会变成“ss”。

规范化文本匹配实用函数

对大多数应用来说,NFC是最好的规范化形式。不区分大小写的比较应该使用str.casefold()。

如果要处理多语言文本,应该有nfc_equal和fold_equal函数

比较规范化Unicode字符串
""" 
Utility functions for normalized Unicode string comparison. 
 
Using Normal Form C, case sensitive: 
 
    >>> s1 = 'café' 
    >>> s2 = 'cafe\u0301' 
    >>> s1 == s2 
    False 
    >>> nfc_equal(s1, s2) 
    True 
    >>> nfc_equal('A', 'a') 
    False 
 
Using Normal Form C with case folding: 
 
    >>> s3 = 'Straße' 
    >>> s4 = 'strasse' 
    >>> s3 == s4 
    False 
    >>> nfc_equal(s3, s4) 
    False 
    >>> fold_equal(s3, s4) 
    True 
    >>> fold_equal(s1, s2) 
    True 
    >>> fold_equal('A', 'a') 
    True 
 
""" 
 
from unicodedata import normalize 
 
def nfc_equal(str1, str2): 
    return normalize('NFC', str1) == normalize('NFC', str2) 
 
def fold_equal(str1, str2): 
    return (normalize('NFC', str1).casefold() == 
            normalize('NFC', str2).casefold())

极端规范化:去掉变音符号

#去掉全部组合记号的函数
import unicodedata 
import string 
 
 
def shave_marks(txt): 
    """去掉全部变音符号""" 
    norm_txt = unicodedata.normalize('NFD', txt)  #➊ 
    shaved = ''.join(c for c in norm_txt 
                     if not unicodedata.combining(c))  #➋ 
    return unicodedata.normalize('NFC', shaved)  #➌

➊把所有字符分解成基字符和和组合记号
➋ 过滤掉所有组合记号
➌重组所有字符

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章