規範化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)  #➌

➊把所有字符分解成基字符和和組合記號
➋ 過濾掉所有組合記號
➌重組所有字符

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