如何解釋 Unicode 正規化,以及 NFC、NFD、NFKC、NFKD 的差異?
題目與使用情境
兩個字串視覺上相同,卻在資料庫唯一約束、搜尋或檔名比較中不相等。請解釋組合字元與預組字元、四種正規化形式、輸入與儲存邊界,並說明為何正規化不能取代大小寫折疊、語言規則或安全策略。
面試官考察什麼
- 是否理解正規等價與相容等價,而非只記縮寫。
- 能否按業務語意選擇 NFC、NFD、NFKC 或 NFKD。
- 是否考慮 Unicode 版本、資料庫排序規則、索引與跨系統一致性。
- 能否指出正規化可能遺失排版或相容字元資訊的風險。
作答前的釐清問題
先確認欄位是展示文字、登入識別、搜尋鍵、檔名還是安全敏感識別;確認語言、大小寫、Unicode 版本、資料庫排序規則與是否需保留原文。不同欄位可以保留原文並另存正規化比較鍵。
30 秒回答框架
Unicode 允許同一可見文字由不同碼點序列表達。NFC 做正規分解後再組合,NFD 做正規分解;NFKC 與 NFKD 額外處理相容等價,可能把外觀相近但語意不同的字元折疊。一般文字常用 NFC;相容折疊只應在明確接受資訊損失時使用。正規化結果應固定 Unicode 版本,並與大小寫折疊、文字腳本及安全檢查分開。
分步驟深入解答
1. 正規等價與組合
預組字元和基礎字元加組合標記可能視覺相同,屬於正規等價。NFD 將它們拆開,NFC 再按規則組合。正規化應具備冪等性:同一形式再次處理不會繼續改變結果。
2. 相容等價的代價
NFKD 先做相容分解,NFKC 再組合。例如某些字型變體、全形形式或裝飾字元可能映射到基礎表示。它適合搜尋或寬鬆比較的明確情境,不應預設用於密碼、法律文字或必須保留原貌的內容。
3. 儲存與安全邊界
可以保存原始輸入,同時產生版本化正規化鍵用於比較。唯一性檢查、搜尋分詞、大小寫折疊與腳本混淆偵測應分別定義。對登入名、網域或權限識別,必須遵循協定與安全規範,不要自行把 NFKC 當成完整防護。
高品質示範回答
我會先區分展示值與比較值。Unicode 允許預組字元與組合序列表達同一正規等價文字,因此直接按碼點比較會產生假不一致。NFD 做正規分解,NFC 再組合;NFKD/NFKC 還處理相容等價,可能犧牲字型或格式資訊。普通文字通常選 NFC,並固定實作支援的 Unicode 版本;搜尋鍵可在明確需求下使用 NFKC,再結合大小寫折疊與語言規則。系統保留原文,另外存正規化鍵與版本,資料庫唯一索引使用後者。對密碼、簽章、稽核原文與安全識別,我不會未經協定規定就做相容折疊,而會執行該情境的規範與混淆檢查。測試包括不同組合順序、重複正規化、跨語言輸入、版本升級與資料庫排序規則差異。
常見錯誤
- 說 NFC 與 NFKC 完全等價。
- 把正規化當作大小寫折疊、轉寫或去除重音。
- 對所有欄位無條件使用 NFKC,導致相容字元資訊遺失。
- 只在應用層正規化,忽略資料庫索引與其他服務的版本。
- 把視覺相同直接當成安全上等同,忽略腳本混淆。
追問及應對
為什麼不能只保存正規化後的字串?
展示、稽核或法律情境可能需要原始輸入;保留原文並存比較鍵可避免不可逆轉換影響體驗,同時支援穩定唯一性檢查。
Unicode 版本升級會破壞唯一約束嗎?
可能改變未分配碼點或正規資料處理。記錄正規化版本,升級前離線重算比較鍵並檢查衝突,再分階段切換索引。
正規化能解決同形異義字元攻擊嗎?
不能完全解決。正規化處理特定等價關係,還需要腳本限制、混淆偵測、協定規定的識別碼設定檔與安全審查。