時隔多年又遇到造字問題,這次的問題是必須精準到把BIG-5時期的四大造字區間的每一個字精準的列舉出來,不能多也不能少共6217個字,也順便把以前寫的程式碼用新語法糖精進一下,將原本用的委派寫法拿掉。
在進行這篇程式碼解決方案的閱讀之前,建議先去之前我的文章中閱讀相關造字編碼的知識,不然你可能會看不懂我在寫啥,文章的參考詳見文末連結。總而言之,傳統認知上的四大造字範圍如下:
FA40-FEFE 785 8E40-A0FE 2983 8140-8DFE 2041 C6A1-C8FE 408 合計 6217
以第一段FA40-FEFE共785個字來說,內部範圍又被劃分如下:
FA40-FA7E 63 FAA1-FAFE 94 FB40-FB7E 63 FBA1-FBFE 94 FC40-FC7E 63 FCA1-FCFE 94 FD40-FD7E 63 FDA1-FDFE 94 FE40-FE7E 63 FEA1-FEFE 94 合計 785
由於運算過程中會不斷發生字串等級的16進制與數值等級的10進制的轉換動作,因此先將兩個轉換方法撰寫如下:
private static int S2I(string x) => System.Int32.Parse(x, System.Globalization.NumberStyles.HexNumber); private static string I2S(int x) => x.ToString("X");
這次要建立的是更精準的6217個字的列舉,真實的實作0x40與0xA1的段落切分,而非先前的粗糙版本一股腦全滾動。
private static System.Collections.Generic.List<string> GenerateEudcDetailRange() { //定義傳統意義上BIG-5造字區間 var oList = new System.Collections.Generic.List<(string cRangeStart, string cRangeEnd)>() { ("FA40", "FEFE"), ("8E40", "A0FE"), ("8140", "8DFE"), ("C6A1", "C8FE"), }; //逐一計算每一個編碼是否合法 var oDetailList = new System.Collections.Generic.List<string>(); foreach (var oItem in oList) { for (int i = S2I(oItem.cRangeStart); i <= S2I(oItem.cRangeEnd); i++) { var cCode = I2S(i); bool bIsLegal = S2I(cCode.Substring(2, 2)) switch { >= 0x40 and <= 0x7E => true, >= 0xA1 and <= 0xFE => true, _ => false }; if (bIsLegal) { oDetailList.Add(cCode); } } } return oDetailList; }
EUDC轉換UNICODE對應編碼說明如下:
private static int Eudc2Unicode(string cEudc) { var oEudc = (iHigh: S2I(cEudc.Substring(0, 2)), iLow: S2I(cEudc.Substring(2, 2))); return oEudc switch { var x when x.iHigh >= 0xFA && x.iHigh <= 0xFE => 0xE000 + (157 * (x.iHigh - 0xFA)) + ((x.iLow < 0x80) ? (x.iLow - 0x40) : (x.iLow - 0x62)), var x when x.iHigh >= 0x8E && x.iHigh <= 0xA0 => 0xE311 + (157 * (x.iHigh - 0x8E)) + ((x.iLow < 0x80) ? (x.iLow - 0x40) : (x.iLow - 0x62)), var x when x.iHigh >= 0x81 && x.iHigh <= 0x8D => 0xEEB8 + (157 * (x.iHigh - 0x81)) + ((x.iLow < 0x80) ? (x.iLow - 0x40) : (x.iLow - 0x62)), var x when x.iHigh >= 0xC6 && x.iHigh <= 0xC8 => 0xF672 + (157 * (x.iHigh - 0xC6)) + ((x.iLow < 0x80) ? (x.iLow - 0x40) : (x.iLow - 0x62)), _ => 0x0 }; }
有了上面各函式的鋪墊,接下來精準取得BIG-5 EUDC造字列表也不是甚麼困難的事情了。
var oEudcCodeList = GenerateEudcDetailRange(); foreach (string cEudc in oEudcCodeList) { Console.WriteLine($"{cEudc}\t{Eudc2Unicode(cEudc)}\t{System.Convert.ToChar(Eudc2Unicode(cEudc)).ToString()}"); }
輸出長得像下列這樣,如果不相信可以轉換到檔案上,可以發現會精準列舉6217個字。
FA40 57344 FA41 57345 FA42 57346 FA43 57347 FA44 57348 FA45 57349 ... 共 6217 個字