在與很多系統做資料交換,有些交換檔是以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下的GetString或GetBytes這兩個方法就可以完成,在.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));
}