專案裡遇到的特殊需求,有個欄位在程式內部採用Unicode編碼,但要匯出給某個系統接收時,中介資料表卻制定了採Big5編碼的VARCHAR(50)格式,這意味著:

1) Big5編碼的中文字元相當於VARCHAR(2),長度限制應為25個Unicode中文字元
2) 包含Unicode的字串在轉換後將變成"?"

原本系統採保守策略,把輸入欄位的長度限定在25個字元,不過客戶反應輸入內容常中英文交雜,英文字元也被視為2個Byte(在VARCHAR其實只佔1個Byte),讓可用字數變少,平白浪費讓說明文字更完整的機會。

所以需求來了: "文字長度限制應由NVARCHAR(25)放寬為VARCHAR(50),並可自動截除過長部分"! 其中的玄機在於英數符號在VARCHAR的長度只有中文的一半,所以若字串中有英數字或半形符號,便可輸入超過25個字。另外,若匯出時發現字串超長可以直接截除,需在後方加上"..."刪節號。

原則上用Encoding.GetEncoding(“big5”).GetBytes()將字串轉成byte[]便可精確計算轉為VARCHAR後的長度,再用GetString(byte[], 0, len)只取限定長度內容轉回字串即可完成自動截除。唯一的小技巧是在截斷時有可能切到中文字,只留下中文字的前一個Byte產生亂碼或問號。我想到的解決方法是用StartsWith比對完整轉換及截斷長度轉換出來的字串,若出現亂碼時,StartsWith會傳回false,此時將len - 1,再多截去殘留的半個中文字,就可以避免中文字被切一半的問題。

附上程式範例:

using System;
using System.Text;
 
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string s = "ABC中文測試";
            Console.WriteLine(TrimForBig5(s, 20, false));
            Console.WriteLine(TrimForBig5(s, 8, false));
            Console.WriteLine(TrimForBig5(s, 8, true));
            Console.Read();
        }
 
        /// <summary>
        /// 將字串截至Big5編碼下的指定長度
        /// </summary>
        /// <param name="s">輸入字串</param>
        /// <param name="len">指定長度</param>
        /// <returns>處理結果</returns>
        static string TrimForBig5(string str, int len, bool ellipsis)
        {
            Encoding big5 = Encoding.GetEncoding("big5");
            byte[] b = big5.GetBytes(str);
            if (b.Length <= len) //未超長,直接傳回
                return str;
            else
            {
                //如果要加刪節號再扣三個字元
                if (ellipsis) len -= 3;
 
                string res = big5.GetString(b, 0, len);
                //由於可能最後一個字元可能切到中文字的前一碼形成亂碼
                //透過截斷的亂碼與完整轉換結果會有出入的原理來偵測
                if (!big5.GetString(b).StartsWith(res))
                    res = big5.GetString(b, 0, len - 1);
                return res + (ellipsis ? "..." : "");
            }
        }
 
    }
}

Comments

# by Ark

why not if (b.Length <= len) //未超長,直接傳回 ??

# by Jeffrey

to Ark, 是的,該處應寫成<=較佳,感謝提醒,已更正。

# by c#新手

正在煩惱要怎麼處理這個問題 想不到剛好能看到這個教學,還有完整程式碼跟說明 真是太謝謝了

Post a comment