現有文字檔“test.txt”(使用vc儲存的,具體格式編碼未知),只有2行字串,如所示:
圖1
其行資料是按照一定格式排列的,前6個位元組表示是人名(字串),後2個位元組表示年齡(整形值)。例如第一行“熊選文28”,表示熊選文的年齡是28歲。注意,第二行其中的”?”不是中文的問號,也不是英文的問號,只是Windows系統沒有對應字元,才顯示? 。如,該“?”對應的兩個位元組:C6 32。
圖2
根據的16進位位元組編碼推測,該文本應該不是UNICODE編碼,因為頭部沒有BOM。
現在要求用C#編寫程式來讀上面這個文字檔,並將每行的人名和年齡分別提取出來。
很簡單,用StreamReader就可以,代碼如下:
public void ReadFileData(string fileName)
{
//System.Text.Encoding encode=System.Text.Encoding.Default;
System.Text.Encoding encode=System.Text.Encoding.GetEncoding("gb2312");
//default表示使用作業系統的編碼即可,一般中文作業系統都是Encoding.GetEncoding("gb2312"),但是其他系統就不一樣了
//所以,此處其實用Encoding.GetEncoding("gb2312")更精確一些,免得有時候換成英文或其他動作系統,讀取資料就有問題了
using(StreamReader sr = new StreamReader(fileName, encode))
{
if (sr == null)
{
return;
}
int nRow=0;//行號
string sLineBuf = null;//行資料緩衝
string sName = "";//人名
int nAge = 0;// 年齡
while ((sLineBuf = sr.ReadLine()) != null)//
{
nRow++;
if (sLineBuf == "")
continue;//如果為空白字串,跳過當前行
sName =GetSubString(sLineBuf, 0, 6);//讀取人名(前6個位元組)
string sAge =GetSubString(sLineBuf, 6, 2);//後2個位元組
nAge = Convert.ToInt16(sAge);//讀取年齡
}
}
}
因為字串內建的取子字串的函數Substring()是按字元數來截取的,而不是按照位元組數來截取。故我自己寫了一個按照位元組數來截取子字串的函數:GetSubString。
/// <summary>
/// 從一個字串中某個位置(該位置以位元組數而不是字元數計算)開始取定長(該字串的長度以位元組數而不是字元數計算)的字串
/// </summary>
/// <param name="SStr">源字串</param>
/// <param name="nStart">取字串的起始位置</param>
/// <param name="nByte">字串的長度</param>
/// <returns>返回字串</returns>
public static string GetSubString(string SStr,int nStart,int nByte)
{
string Tstr = "";
byte[] sbytes = System.Text.Encoding.GetEncoding("gb2312").GetBytes(SStr);//轉換為位元組數組
if (sbytes.Length == 0)
return Tstr;
if (nStart > sbytes.Length)
return Tstr;
byte[] tbytes = new byte[nByte];
int i = nStart;
int j = 0;
while (i < sbytes.Length && j < nByte)
{
tbytes[j++] = sbytes[i++];
}
try
{
Tstr = System.Text.Encoding.GetEncoding("gb2312").GetString(tbytes);//轉換為字串
}
catch (System.Exception ex)
{
throw ex;
}
return Tstr;
}
上面的程式寫好了,開始讀取資料,輸出結果如下:
人名:年齡
熊選文:28
張三?2:0
第一行資料是沒問題,但第二行就有問題了。本來應該是"張三?"的年齡是"20"歲,讀出來的結果是"張三?2"的年齡是"0"歲,明顯有誤。為什麼會出現這樣的結果呢?
通過調試,就會發現c#讀取該行的十六進位為:D5 C5 C8 FD 3F 32 30,而原本應該是D5 C5 C8 FD C6 32 32 30(見圖2)。原本的C6 32變成了3F(3F對應的字元就是英文的問號),所以位元組數就少了一個,截取前6個位元組就是D5 C5 C8 FD 3F 32,對應的字串就是“張三?2”,後面只有一個位元組30,對應的字元就是0。所以最後得到的結果是“張三?2”的年齡是“0”。
通過對比分析發現,第一行資料中沒有像“C6 32”這樣的字元,所以用c#讀出來結果很正常。第二行中有“C6 32”這樣的字元,用C#讀就會自動轉換為3F。如果把“C6 32”換成其他的字元,如“BD 32”,都會有同樣的問題。這是為什麼呢?我想,應該是在GB2312中沒有這種字元編碼,所以無法識別,將該字元自動轉換為英文的”?”,當然也無法正確讀和顯示了。
像上面這種文本資料(文本編碼方式未知,但包含中英文,甚至有除中英文之外的其他字元),用C#如何才能無誤的讀取資料呢?