中英數文字混雜的截字或補空白方法[Big5版]

  • 12317
  • 0
  • .Net
  • 2010-12-03

在與很多系統做資料交換,有些交換檔是以Big5編碼的固定Bytes的定寬格式,因為在.NET中任何文字包含中英數字或符號都是一個Char,但Big5編碼英文是1Byte、中文是2Byte,所以不能以文字的長度來截字或補空白,小弟我想到的是轉成Bytes來處理。

在與很多系統做資料交換,有些交換檔是以Big5編碼固定Bytes的定寬格式,因為在.NET中任何文字包含中英數字或符號都是一個Char,但Big5編碼英文是1Byte、中文是2Byte,所以不能以文字的長度來截字或補空白,小弟我想到的是轉成Bytes來處理。

 

程式碼


public static class ChineseHelper
{
    static ChineseHelper()
    {
        Encoding = Encoding.GetEncoding(950); //950是Big5的CodePage
    }

    public static Encoding Encoding { get; set; }

    /// <summary>
    /// 判斷文字的Bytes數有沒有超過上限,有的話截斷,沒有的話補空白
    /// </summary>        
    public static string 截字補空(string value, int maxLength)
    {
        if (string.IsNullOrWhiteSpace(value) || maxLength <= 0)
        {
            return string.Empty;
        }

        var result = 截字Base(value, maxLength);

        if (result.Item2 == 0)
        {
            return result.Item1;
        }
        else
        {
            return result.Item1 + "".PadRight(result.Item2);
        }
    }

    /// <summary>
    /// 判斷文字的Bytes數有沒有超過上限,有的話截斷
    /// </summary>
    public static string 截字(string value, int maxLength)
    {
        if (string.IsNullOrWhiteSpace(value) || maxLength <= 0)
        {
            return string.Empty;
        }

        return 截字Base(value, maxLength).Item1;
    }

    /// <summary>
    /// 給截字補空與截字使用
    /// </summary>
    private static Tuple<string, int> 截字Base(string value, int maxLength)
    {
        int padding = 0;
        var buffer = Encoding.GetBytes(value);
        if (buffer.Length > maxLength)
        {
            int charStartIndex = maxLength - 1;
            int charEndIndex = 0;
            //跑回圈去算出結尾。
            for (int i = 0; i < maxLength; )
            {
                if (buffer[i] <= 128)
                {
                    charEndIndex = i; //英數1Byte
                    i += 1;
                }
                else
                {
                    charEndIndex = i + 1; //中文2Byte
                    i += 2;
                }
            }

            //如果開始不同與結尾,表示截到2Byte的中文字了,要捨棄1Byte
            if (charStartIndex != charEndIndex)
            {
                value = Encoding.GetString(buffer, 0, charStartIndex);
                padding = 1;
            }
            else
            {
                value = Encoding.GetString(buffer, 0, maxLength);
            }
        }
        else
        {
            value = Encoding.GetString(buffer);

            padding = maxLength - buffer.Length;
        }

        return Tuple.Create(value, padding);
    }
}

 

程式解說

String與Bytes轉換

在.NET中Strng轉成Bytes或Bytes轉成String很簡單,只要呼叫System.Text.Encoding下的GetStringGetBytes這兩個方法就可以完成,在.NET包含了超過一百多種的文字編碼,可以用GetEncoding特定編碼,如Big5使用950取得,其他常見的編碼以下表:

名稱 字碼頁
ASCII 20127
UTF-7 65000
UTF-8 65001
UTF-16(LE) 1200
UTF-16(BE) 1200
UTF-32(LE) 65005
UTF-32(BE) 65006
Big5 950
gb2312 936
Shift-JIS 932

 

文字大小判斷

Big5從為了相容ASCII,所以小於129(0x81)的都是1Byte,而大於128(0x80)的都是2Byte,所以我判斷是方式如下:


int charStartIndex = maxLength - 1;
int charEndIndex = 0;
//跑回圈去算出結尾。
for (int i = 0; i < maxLength; )
{
    if (buffer[i] <= 128)
    {
        charEndIndex = i; //英數1Byte
        i += 1;
    }
    else
    {
        charEndIndex = i + 1; //中文2Byte
        i += 2;
    }
}

 

因為1Byte或2Byte混雜,無法從中間得知這1Byte是Big5還是ACSII,所以從0開始算出到maxLength前的文字來判斷文字大小。

長度 1 2 3 4 5 6    7 8    9 10  11
位址 0 1 2 3 4 5    6 7    8 9  10
文字 W a d e  
值(16進位) 57 61 64 65 20 B6 C0 B0 B6 BA 61

1. maxLength=4時charStartIndex =3、charEndIndex=3,可以剛好取出 Wade。

2. maxLength=6時charStartIndex =5、charEndIndex=6,只能取到黃的1Byte,會造成錯誤(會變成?),所以那1Byte要捨去。

3. maxLength=7時charStartIndex =6、charEndIndex=6,可以剛好取出 Wade 黃。

 

測試


[TestMethod(), Owner("Wade")]
public void U__ChineseHelper_截字補空()
{
    Assert.AreEqual("ABCDEFG", ChineseHelper.截字補空("ABCDEFGHIJ", 7));

    Assert.AreEqual("Wade 黃", ChineseHelper.截字補空("Wade 黃偉榮", 7));
    Assert.AreEqual("Wade 黃 ", ChineseHelper.截字補空("Wade 黃偉榮", 8));
    Assert.AreEqual("黃偉 ", ChineseHelper.截字補空("黃偉榮", 5));
    Assert.AreEqual("黃偉榮    ", ChineseHelper.截字補空("黃偉榮", 10));
    Assert.AreEqual("黃1偉2榮3 ", ChineseHelper.截字補空("黃1偉2榮3", 10));
    Assert.AreEqual("黃1偉2榮", ChineseHelper.截字補空("黃1偉2榮3", 8));
    Assert.AreEqual("黃1偉2 ", ChineseHelper.截字補空("黃1偉2榮3", 7));
    Assert.AreEqual("黃!偉 ", ChineseHelper.截字補空("黃!偉!榮!", 7));
    //罕見字
    Assert.AreEqual("黃?榮", ChineseHelper.截字補空("黃犇榮", 5));
    Assert.AreEqual("黃?榮 ", ChineseHelper.截字補空("黃犇榮", 6));
}

[TestMethod(), Owner("Wade")]
public void U__ChineseHelper_截字()
{
    Assert.AreEqual("ABCDEFG", ChineseHelper.截字("ABCDEFGHIJ", 7));

    Assert.AreEqual("Wade 黃", ChineseHelper.截字("Wade 黃偉榮", 7));
    Assert.AreEqual("Wade 黃", ChineseHelper.截字("Wade 黃偉榮", 8));
    Assert.AreEqual("黃偉", ChineseHelper.截字("黃偉榮", 5));
    Assert.AreEqual("黃偉榮", ChineseHelper.截字("黃偉榮", 10));
    Assert.AreEqual("黃1偉2榮3", ChineseHelper.截字("黃1偉2榮3", 10));
    Assert.AreEqual("黃1偉2榮", ChineseHelper.截字("黃1偉2榮3", 8));
    Assert.AreEqual("黃1偉2", ChineseHelper.截字("黃1偉2榮3", 7));
    Assert.AreEqual("黃!偉", ChineseHelper.截字("黃!偉!榮!", 7));
    //罕見字
    Assert.AreEqual("黃?榮", ChineseHelper.截字("黃犇榮", 5));
    Assert.AreEqual("黃?", ChineseHelper.截字("黃犇榮", 4));
}

 

 

參考資料

大五碼 - 維基百科,自由的百科全書
Encoding 類別(System.Text)