使用 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

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


留言
avatar-img
留言分享你的想法!
avatar-img
Justin Shaw's Salon
2會員
7內容數
Justin Shaw's Salon的其他內容
2023/08/06
簡介如何使用 NGROK 來協助開發與測試
Thumbnail
2023/08/06
簡介如何使用 NGROK 來協助開發與測試
Thumbnail
2023/07/28
Hosts File 是一種可以取代 DNS 查詢的步驟 直接指定 domain 所指向的 IP 位址 甚至是不存在的 domain 也可以使用 hosts file 來給定 IP 位址
Thumbnail
2023/07/28
Hosts File 是一種可以取代 DNS 查詢的步驟 直接指定 domain 所指向的 IP 位址 甚至是不存在的 domain 也可以使用 hosts file 來給定 IP 位址
Thumbnail
2023/07/22
講完了 Story 的拆解 其中提到了 Scope 那麼 Scope 是什麼呢? 以及伴隨著 Scope  很常聽到的 Acceptance Criteria (AC) 又扮演了什麼樣的角色? 0x00 回顧 在系列文章中的第一篇 From Scrum to LeSS — Roles
Thumbnail
2023/07/22
講完了 Story 的拆解 其中提到了 Scope 那麼 Scope 是什麼呢? 以及伴隨著 Scope  很常聽到的 Acceptance Criteria (AC) 又扮演了什麼樣的角色? 0x00 回顧 在系列文章中的第一篇 From Scrum to LeSS — Roles
Thumbnail
看更多
你可能也想看
Thumbnail
沙龍一直是創作與交流的重要空間,這次 vocus 全面改版了沙龍介面,就是為了讓好內容被好好看見! 你可以自由編排你的沙龍首頁版位,新版手機介面也讓每位訪客都能更快找到感興趣的內容、成為你的支持者。 改版完成後可以在社群媒體分享新版面,並標記 @vocus.official⁠ ♥️ ⁠
Thumbnail
沙龍一直是創作與交流的重要空間,這次 vocus 全面改版了沙龍介面,就是為了讓好內容被好好看見! 你可以自由編排你的沙龍首頁版位,新版手機介面也讓每位訪客都能更快找到感興趣的內容、成為你的支持者。 改版完成後可以在社群媒體分享新版面,並標記 @vocus.official⁠ ♥️ ⁠
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
一般在使用 TypeScript 的時候,大家都有遇過定義列舉資料的情境吧。 不過不管是 enum 和 literal 的方式其實都有些小缺點,以下推薦一個個人認為體驗更好的方式。
Thumbnail
一般在使用 TypeScript 的時候,大家都有遇過定義列舉資料的情境吧。 不過不管是 enum 和 literal 的方式其實都有些小缺點,以下推薦一個個人認為體驗更好的方式。
Thumbnail
本篇文章講解了字符編碼的基礎知識,包括ASCII, Unicode 和 UTF-8的誕生背景、解決的問題以及轉換方式。瞭解這些知識有助於解決在讀檔案時用錯誤的編碼方式轉換就會出現亂碼等問題。文章內容涉及電腦技術中的字符編碼相關歷史緣由,可幫助讀者解決相關疑問。
Thumbnail
本篇文章講解了字符編碼的基礎知識,包括ASCII, Unicode 和 UTF-8的誕生背景、解決的問題以及轉換方式。瞭解這些知識有助於解決在讀檔案時用錯誤的編碼方式轉換就會出現亂碼等問題。文章內容涉及電腦技術中的字符編碼相關歷史緣由,可幫助讀者解決相關疑問。
Thumbnail
內容涵蓋資料型別、型別轉換、自訂型別、元組型別、集合型別和字典型別等主題。文章首先詳述內建型別如bool、byte、char等的定義和使用,接著討論型別轉換,包括隱含轉換和明確轉換。之後文章介紹自訂型別的建立,以及元組、集合、陣列和字典型別的操作與例子。
Thumbnail
內容涵蓋資料型別、型別轉換、自訂型別、元組型別、集合型別和字典型別等主題。文章首先詳述內建型別如bool、byte、char等的定義和使用,接著討論型別轉換,包括隱含轉換和明確轉換。之後文章介紹自訂型別的建立,以及元組、集合、陣列和字典型別的操作與例子。
Thumbnail
在API介接中使用x-www-form-urlencoded格式時,可能會遇到一些踩坑的情況,本文分享了作者在這方面遇到的問題和解決方法。
Thumbnail
在API介接中使用x-www-form-urlencoded格式時,可能會遇到一些踩坑的情況,本文分享了作者在這方面遇到的問題和解決方法。
Thumbnail
軟體開發時應該要有固定的命名規則,以提高程式的可讀性,本篇文章帶你認識常見的幾個命名方法。
Thumbnail
軟體開發時應該要有固定的命名規則,以提高程式的可讀性,本篇文章帶你認識常見的幾個命名方法。
Thumbnail
f字符串(f-string)在Python 3.6版本引入了新特性,可以更方便地格式化字符串。本文介紹了f-string的基本使用方法,以及表達式、運算符、格式化控制、字典和列表的應用,以及調用方法和函數等。f-string提供了一種更靈活的方式,使你能夠控制字符串的外觀,以滿足不同情況下的需求。
Thumbnail
f字符串(f-string)在Python 3.6版本引入了新特性,可以更方便地格式化字符串。本文介紹了f-string的基本使用方法,以及表達式、運算符、格式化控制、字典和列表的應用,以及調用方法和函數等。f-string提供了一種更靈活的方式,使你能夠控制字符串的外觀,以滿足不同情況下的需求。
Thumbnail
運用 Golang 正則表達式處理文本。從替換操作到 URL 解析,再到日誌分析,掌握實際應用場景下的正則技巧,提取、轉換和分析文本數據。
Thumbnail
運用 Golang 正則表達式處理文本。從替換操作到 URL 解析,再到日誌分析,掌握實際應用場景下的正則技巧,提取、轉換和分析文本數據。
Thumbnail
由於遇到系統不支援歐洲語系的重音符號或變音符號因此有了這篇文章
Thumbnail
由於遇到系統不支援歐洲語系的重音符號或變音符號因此有了這篇文章
Thumbnail
由於Go語言本身沒有提供Enum的功能, 故我們可以使用package及type的技巧來達到類似的功能,假設今天要定義季節的enum型別, 包含了「春、夏、秋、冬」四種值的時候,可以怎麼做呢? 首先我們可以用package來框住season的範圍: 然而在season.go可以定義一個字串的類型 最
Thumbnail
由於Go語言本身沒有提供Enum的功能, 故我們可以使用package及type的技巧來達到類似的功能,假設今天要定義季節的enum型別, 包含了「春、夏、秋、冬」四種值的時候,可以怎麼做呢? 首先我們可以用package來框住season的範圍: 然而在season.go可以定義一個字串的類型 最
Thumbnail
本篇文章將會介紹Switch和Enum,Switch這是一個等同開關的陳述式,相較於頻繁的「if」和「else if」,是一個在眾多方案中切換的一個好用法;Enum是具名常數,可以輔助並提升程式設計的易讀性。
Thumbnail
本篇文章將會介紹Switch和Enum,Switch這是一個等同開關的陳述式,相較於頻繁的「if」和「else if」,是一個在眾多方案中切換的一個好用法;Enum是具名常數,可以輔助並提升程式設計的易讀性。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News