2011年12月16日 星期五

取得繁體中文字元筆劃數

完整的中文筆畫查表方法,已另於 取得繁體中文字元筆劃數 (Unicode) 中提供,此為過渡時期方法,無法處理罕見字集。

專案中的某項功能需要依中文字筆劃數分組顯示,例如:1~5劃、6~10劃...等,因此產生了查得中文字元筆劃的需求。

本來呢寄望 Windows 或 IME 能提供相關的 API,但似乎沒那麼容易,網路上多數是轉 BIG5 後以內碼分區的查表方式取得筆劃數,查到有 PHPC#ActionScriptJava 這些例子,一篇篇讀下來解決方案如出一轍,應該是個穩定的方式吧!?

有了! 其中的 C# 範例是我想要的,但我更想利用 extension method 實作,所以就動手修改了一下代碼如下:

    public static class CharExtension
    {
        private static Encoding _big5 = Encoding.GetEncoding(950);

        /// 
        /// 取得繁中字元筆劃數
        /// 
        /// 
        /// 
        public static int TraditionalChineseStroke(this char chr)
        {
            var bytes = _big5.GetBytes(new[] { chr });

            if (bytes.Length == 1)
            {
                // 傳入一般的 ASCII 字元,如:A-F, 0-9 之類的
                return 0;
            }

            var code = (bytes[0] * 256) + bytes[1];

            return TraditionalChineseStrokeLookup(code);
        }

        /// 
        /// 來自 GitHub 上有其他專案準備好的 big5_stroke.tab https://gist.github.com/1150937/ 
        /// 
        /// 
        /// 
        private static int TraditionalChineseStrokeLookup(int code)
        {
            if (code >= 0xa440 && code <= 0xa441) { return 1; }
            if (code >= 0xa442 && code <= 0xa453) { return 2; }
            if (code >= 0xa454 && code <= 0xa47e) { return 3; }
            if (code >= 0xa4a1 && code <= 0xa4fd) { return 4; }
            if (code >= 0xa4fe && code <= 0xa5df) { return 5; }
            if (code >= 0xa5e0 && code <= 0xa6e9) { return 6; }
            if (code >= 0xa6ea && code <= 0xa8c2) { return 7; }
            if (code >= 0xa8c3 && code <= 0xab44) { return 8; }
            if (code >= 0xab45 && code <= 0xadbb) { return 9; }
            if (code >= 0xadbc && code <= 0xb0ad) { return 10; }
            if (code >= 0xb0ae && code <= 0xb3c2) { return 11; }
            if (code >= 0xb3c3 && code <= 0xb6c3) { return 12; }
            if (code >= 0xb6c4 && code <= 0xb9ab) { return 13; }
            if (code >= 0xb9ac && code <= 0xbbf4) { return 14; }
            if (code >= 0xbbf5 && code <= 0xbea6) { return 15; }
            if (code >= 0xbea7 && code <= 0xc074) { return 16; }
            if (code >= 0xc075 && code <= 0xc24e) { return 17; }
            if (code >= 0xc24f && code <= 0xc35e) { return 18; }
            if (code >= 0xc35f && code <= 0xc454) { return 19; }
            if (code >= 0xc455 && code <= 0xc4d6) { return 20; }
            if (code >= 0xc3d7 && code <= 0xc56a) { return 21; }
            if (code >= 0xc56b && code <= 0xc5c7) { return 22; }
            if (code >= 0xc5c8 && code <= 0xc5c7) { return 23; }
            if (code >= 0xc5f1 && code <= 0xc654) { return 24; }
            if (code >= 0xc655 && code <= 0xc664) { return 25; }
            if (code >= 0xc665 && code <= 0xc66b) { return 26; }
            if (code >= 0xc66c && code <= 0xc675) { return 27; }
            if (code >= 0xc676 && code <= 0xc67a) { return 28; }
            if (code >= 0xc67b && code <= 0xc67e) { return 29; }
            if (code >= 0xc940 && code <= 0xc944) { return 2; }
            if (code >= 0xc945 && code <= 0xc94c) { return 3; }
            if (code >= 0xc94d && code <= 0xc95c) { return 4; }
            if (code >= 0xc95d && code <= 0xc9aa) { return 5; }
            if (code >= 0xc9ab && code <= 0xc959) { return 6; }
            if (code >= 0xca5a && code <= 0xcbb0) { return 7; }
            if (code >= 0xcbb1 && code <= 0xcddc) { return 8; }
            if (code >= 0xcddd && code <= 0xd0c7) { return 9; }
            if (code >= 0xd0c8 && code <= 0xd44a) { return 10; }
            if (code >= 0xd44b && code <= 0xd850) { return 11; }
            if (code >= 0xd851 && code <= 0xdcb0) { return 12; }
            if (code >= 0xdcb1 && code <= 0xe0ef) { return 13; }
            if (code >= 0xe0f0 && code <= 0xe4e5) { return 14; }
            if (code >= 0xe4e6 && code <= 0xe8f3) { return 15; }
            if (code >= 0xe8f4 && code <= 0xecb8) { return 16; }
            if (code >= 0xecb9 && code <= 0xefb6) { return 17; }
            if (code >= 0xefb7 && code <= 0xf1ea) { return 18; }
            if (code >= 0xf1eb && code <= 0xf3fc) { return 19; }
            if (code >= 0xf3fd && code <= 0xf5bf) { return 20; }
            if (code >= 0xf5c0 && code <= 0xf6d5) { return 21; }
            if (code >= 0xf6d6 && code <= 0xf7cf) { return 22; }
            if (code >= 0xf6d6 && code <= 0xf7cf) { return 23; }
            if (code >= 0xf8a5 && code <= 0xf8ed) { return 24; }
            if (code >= 0xf8e9 && code <= 0xf96a) { return 25; }
            if (code >= 0xf96b && code <= 0xf9a1) { return 26; }
            if (code >= 0xf9a2 && code <= 0xf9b9) { return 27; }
            if (code >= 0xf9ba && code <= 0xf9c5) { return 28; }
            if (code >= 0xf9c6 && code <= 0xf9dc) { return 29; }
            if (code >= 0xf9da && code <= 0xf9da) { return 9; }
            if (code >= 0xf9db && code <= 0xf9db) { return 12; }
            if (code >= 0xf9d6 && code <= 0xf9d8) { return 13; }
            if (code >= 0xf9dc && code <= 0xf9dc) { return 15; }
            if (code >= 0xf9d9 && code <= 0xf9d9) { return 16; }
            if (code >= 0xc67b && code <= 0xc67d) { return 30; }
            if (code >= 0xf9cc && code <= 0xf9cf) { return 30; }
            if (code >= 0xf9c6 && code <= 0xf9c6) { return 31; }
            if (code >= 0xf9d0 && code <= 0xf9d0) { return 31; }
            if (code >= 0xf9d1 && code <= 0xf9d1) { return 32; }
            if (code >= 0xc67e && code <= 0xc67e) { return 33; }
            if (code >= 0xf9d2 && code <= 0xf9d2) { return 33; }
            if (code >= 0xf9d3 && code <= 0xf9d3) { return 34; }
            if (code >= 0xf9d4 && code <= 0xf9d5) { return 36; }

            return 0;
        }
    }

經過一番測試,這方法不盡理想,例如:「堃」字在 BIG5 中並不存在,因此在轉換到 BIG5 bytes 的過程中無法順利轉換為雙位元(而是轉成 "?" 的 ASCII code 63),為此我應該會尋找更合適的方法,整理後再分享。

查表資料參考 GitHub 上其他專案準備好的 big_stroke.tab,由於不認為這份資料會有異動性,所以就不想由外部載入了,用 regular expression 稍微動手一下,整理起 code 挺方便 :)

沒有留言: