Big5編碼轉換字串及Byte時遇到的小問題
分享一個最近遇到的問題
在跟一些機關交換資料時,滿常會遇到使用固定寬度的文字檔作為交換媒介
而這些文字檔大部分都是使用Big5編碼的,而今天遇到的問題是:
假設此文字檔有兩個欄位:姓名、生日
姓名的長度規定是 10 個byte
生日的長度規定是 7 個byte,格式是 yyyMMdd
以下程式是以LinqPad作的範例
原始資料
void Main()
{
//使用Big5編碼
Encoding big5=Encoding.GetEncoding(950);
//姓名欄位
var name = "ILOVE邱若男";
//生日欄位
var birthday ="0740628";
}
//將文字使用特定編碼轉為Byte[] 並取特定的長度
public byte[] SplieStringToByteArray(string source,int count,Encoding encoding)
{
return encoding.GetBytes(source).Where((p,index)=>index < count).ToArray();
}
將欄位的值依Big5編碼轉為Byte[]後擷取長度
var byteName = SplieStringToByteArray(name,10,big5);
var byteBirthday = SplieStringToByteArray(birthday,7,big5);
var data = byteName.Concat(byteBirthday).ToArray();
("原始資料長度: " + big5.GetBytes(name+birthday).Length).Dump();
("原始資料: " + (name+birthday)).Dump();
("截斷後資料長度: "+data.Length).Dump();
("截斷後資料: "+big5.GetString(data)).Dump();
可以看到因為"ILOVE邱若男" byte[]的長度是11,但格式規定總長度只要10
因此"男"這個字被切了一半,原本"男"在Big5的編碼是A8 6B,截斷後變為A8
想當然爾,將此byte[]轉回string後,"男"字就變問號了。
到這邊為止,都還不是我遇到的問題,因為某機關傳給我的資料
就是長度為17的 "ILOVE邱若?740628"
而收到資料後,因為是收到文字檔,因此很自然地用
File.ReadAllText("..path",Encoding.GetEncoding(950));
這樣的方法去讀,但是這樣的做法,卻發現再將文字轉回byte[]時長度少了1
//假設讀出檔案為string
var readFile = big5.GetString(data);
("將文字轉為Byte[]後的長度: " +big5.GetBytes(readFile).Length).Dump();
詭異吧,明明是同一組byte[]在轉來轉去,但長度就少了1
但是看看下圖,就可以知道為什麼了
因為原本的"男"字編碼是A8 6B,截斷後只剩下A8,但在轉換為string的時候
因Big5是一套雙位元組字符,高低位位元組結合後轉換,因此A8跟後面的30被湊做堆,
不過並沒有A8 30這個編碼,所以轉出來依然是"?",但"0"這個數字就被吃掉了。
而"ILOVE邱若?740628"再轉為byte[]時,?只算一個byte,因此長度就變為16了。
因此在處理這種類型的檔案時,最好不要將檔案讀成string再轉換為byte[]
而是直接用File.ReadAllBytes()的方式,將所有位元讀進來後,依指定的長度裁切後再轉回string
("讀取姓名(長度10): " + SplieByteArrayToString(data,0,10,big5)).Dump();
("讀取生日(長度7): " + SplieByteArrayToString(data,10,7,big5)).Dump();
public string SplieByteArrayToString(byte[] source,int startIndex, int length ,Encoding encoding)
{
var spliteByte=source.Where((p,index)=> index >= startIndex &&
index < (startIndex+length))
.ToArray();
return encoding.GetString(spliteByte);
}
這樣才能避免因被裁切的中文字跟後面的位元結合後轉換錯誤的問題
相關連結