使用 C# 移除字串中的重音/變音符

閱讀時間約 6 分鐘

0x00 前情提要

最近正在嘗試將 WebApi 改用 GraphQL
並使用了 HotChocolate 套件來自動生成整個 GraphQL 文件
But!
原以為一切應該要很順利的
卻遇到了一個令人困惑的錯誤訊息

HotChocolate.SchemaException: For more details look at the `Errors` property.
1. The specified name is not a valid GraphQL name. (Parameter 'value') (HotChocolate.Types.EnumType<MyEnum>)

起初我以為將 Enum 值轉換成字串只不過是 ToString() 而已
實在是不解為什麼會發生錯誤?


0x01 開始抓蟲

由於原本的 Enum 很大一包
所以我快速地掃了一遍並沒有發現任何明顯的問題
尤其整包 Enum 都是品牌名稱
直覺上不會有問題才對

後來使用了中斷點的方式抓出造成錯誤的 Enum
原來有一個 Enum 值的名稱包含了變音符號

來個例子🌰:

enum Brand 
{
// …
Hermès,
// …
}

當 Hermès ToString() 轉換為 GraphQL 的 Enum Type 時
就違反了命名規範因此噴錯


0x02 殺蟲時間

其實最簡單暴力的解決方法是建立一個對照表
將所有帶有變音符號的字母直接替換成相應的字母
但這方案不是很優雅

因此持續尋找答案的過程中
發現 Unicode 有四種不同的正規化形式
(Normalization Form,簡稱 NFC、NFD、NFKC、NFKD)

Ref:
1. .NET NormalizationForm Enum
2. UNICODE NORMALIZATION FORMS

對於處理變音符號,我們需要使用 NFC 和 NFD 這兩種形式。

static void PrintAsBytes(string s, NormalizationForm form)
{
Console.WriteLine($"{form}: {string.Join(" ", s.Normalize(form).Select(s => $"{(short)s:X4}"))}");
}
PrintAsBytes("è", NormalizationForm.FormC);
PrintAsBytes("è", NormalizationForm.FormD);

建立一個方法將不同 NormalizationForm 的字串用 bytes 的方式印出來
可以得到結果為

FormC: 00E8
FormD: 0065 0300

而 ASCII 中的 e 就是 0x65
由此可見我們可以利用 FormD 的格式找到無變音符號的字母
將變音符號去除之後就可以得到符合 ASCII 範圍內的字母了

根據上面的想法可以寫一段這樣的方法來實作

static string RemoveDiacritics(string s) {
return string.Concat(Regex.Matches(s.Normalize(NormalizationForm.FormD), @"[A-Za-z0-9_]").Normalize(NormalizationForm.FormC);
}

但這樣的做法閱讀起還有那麼一點彆扭
後來發現微軟有提供 CharUnicodeInfo.GetUnicodeCategory 的方法
利用 UnicodeCategory 來識別是否應該去除

static string RemoveDiacritics(string s) {
return string.Concat(s.Normalize(NormalizationForm.FormD).Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)).Normalize(NormalizationForm.FormC);
}

這樣的程式碼更容易閱讀
讓意圖更加明確


0x03 Unicode Ascii Folding Filter

上面有提到的其中一種做法是用對照表的方式做替換
而其實在查找的過程中
發現 Apache 專案中有個 ASCII Folding Filter 的方法
就是用超大的 switch case 製作的
網路上也有人把這個邏輯改寫成 C# 版本
但後來沒有使用這個版本
因為 ASCII Folding Filter 做的事情不只移除變音符號
還處理了所有的 Unicode 的類型
這是擷取 source code 中的註解內容

This class converts alphabetic, numeric, and symbolic Unicode characters which are not in the first 127 ASCII characters (the “Basic Latin” Unicode block) into their ASCII equivalents, if one exists.

如果是文章或是使用者輸入的文字
要轉進只允許 ASCII 的系統會比較合適


0xFF 後記

這次的問題出自於在建立這個 Enum 時
大家都是習慣性的複製貼上
由於這次提供原始資料的來源是歐洲公司
因此名稱上就容易出現變音符
而現在多數的程式語言也都支援 Unicode
所以貼上時也不會出問題

這時突然想到一件有趣的事情
例如 Swift 甚至可以用 emoji 來寫 code

raw-image

ref: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics/#Naming-Constants-and-Variables

就嘗試了一下看看 C# 的 enum 是不是也可以用 emoji
經過嘗試之後發現
中文是可以的
但 emoji 不行
或 unicode 的符號 (e.g. ①) 也不行
查了一下原因找到了一個 GitHub 的討論串
滿有趣的

raw-image

希望這篇文章的小發現你也會覺得有趣


    留言0
    查看全部
    發表第一個留言支持創作者!
    你可能也想看
    寶寶幾歲看得到?兒童晶亮有神 6大方法!2 歲前不要使用3C...寶寶隨著年齡日漸成長,剛出生的嬰兒只有黑白世界, 3-6 個月慢慢可以看到紅、黃、藍、綠等顏色、1 歲左右才能看到清楚且充滿色彩的世界!近幾年宅在家,再加上遠距教學,使用平板、電腦、手機的時間大幅增加
    Thumbnail
    avatar
    BabyLike育兒親子網
    2023-03-25
    使用3C要適度,避免眼睛受傷害,醫師圖解長時間接觸3C產品有逐漸年輕化及普遍化的趨勢,3C產品所帶來的健康危害,是不容忽視的課題。過度近距離長時間使用3C產品,眼睛不自覺用力,易導致眼軸拉長及近視度數快速加深,嚴重時會導致黃斑部傷害,增加視網膜剝離及失明的風險!
    Thumbnail
    avatar
    照護線上
    2022-07-25
    【數位教養不卡關 #1】王宏哲:孩子為何沉迷網路和手機?家庭使用3C建議你是否擔心孩子有網路沉迷的狀況?要如何減低家中兒童3C網路沉迷的風險呢? 對此,兒童發展專家王宏哲便分享了自身的觀察及好用的3C親子溝通法供各位家長參考。其認為家長可從「依年齡層調整使用3C時間」、「和孩子的約定」兩個面向著手!
    Thumbnail
    avatar
    台灣展翅協會
    2022-07-21
    孩子使用3C的時間與頻率適當嗎?~談父母如何制定孩子使用3C的規則父母如何制定3C使用規則,需瞭解自己給予孩子使用3C的目的與時機,並制定好頻率與時間,確實執行並監督管理。
    Thumbnail
    avatar
    成材不成癮
    2022-05-23
    Dart 如何使用C語言函式庫 (Windows)  雖然Dart 語言本身支援跨平台的編譯方式,但在實務開發時還是不免需要使用外部非Dart語言所提供的函式庫進行功能開發且由於C 語言是最為廣泛且通用的程式語言,因此Dart語言也有提供支援與C語言函式庫互通性的方式;本篇主要是以MSVC作為C的編譯器來實作說明如何引用C語言會遇到的作法。
    Thumbnail
    avatar
    跨碼軟體有限公司
    2022-04-22
    該給孩子用3C嗎? 建立孩子使用3C小原則3C除了會侵襲孩子的視力,連情緒、專注力、人際互動都會受影響, 但要因此禁止小孩接觸3C產品嗎? 其實3C並不可怕,而是錯誤的使用情境~ 🙋‍♀️本篇作伴要來給 爸比媽咪們建立孩子使用3C的小原則, 一起來為孩子的健康把關吧💓
    Thumbnail
    avatar
    作伴 ZuoBann
    2022-04-19
    🌱般若波羅蜜多 - 💛經 (可下載使用,不可移除商標、LOGO,不可修改任何內容般若波羅蜜多 - 心經 (可下載使用,不可移除商標、LOGO,不可修改任何內容
    Thumbnail
    avatar
    💫 星星的奇幻旅程 🌌
    2022-02-20
    孩子使用3C,行?不行?使用3C,對我來說是一種矛盾。 一方面,我不覺得用禁止的方式就會有好結果,畢竟總有一天孩子會接觸3C,雖然有人說過等孩子大一點再接觸也不遲,但我可以讓孩子二歲左右就使用剪刀,五歲時由他在後陽台用鐵槌和釘子釘木板⋯⋯,但為何面對3C產品就開始猶豫了?
    Thumbnail
    avatar
    思嘉
    2019-03-30