首先如果讀者對編碼或BOM還不熟悉的話,推薦先讀這篇文章:.NET(C#):字元編碼(Encoding)和位元組順序標記(BOM)。
中文編碼基本上可以分成兩大類:
1. ANSI編碼的擴充集合:例如GBK, GB2312, GB18030等,這類編碼都不存在BOM(一些更新的標準中文編碼,例如GB18030和GBK編碼,都向後相容GB2312編碼)。
2. Unicode編碼集合:如UTF-8, UTF-16, UTF-32等。這類編碼可以有BOM,也可以不加BOM。
3. 部分Unicode編碼還存在具體字節次序問題(Endianess),就是所謂的Little endian和Big endian之分,不同此節次序對於不同的BOM,比如UTF16,不過UTF8不存在字節次序問題。
OK,了解了基本知識後,讓我們回到主題,該如何正確開啟中文文字檔案。第一個需要確認的訊息是:你的Unicode編碼檔案是否包含BOM?
如果包含BOM的話,那麼一切好說!因為如果我們發現了BOM,我們就知道他的具體編碼了。如果沒有發現BOM,那就不是Unicode,我們用系統預設的ANSI擴充中文編碼集打開文字檔就OK了。
而如果Unicode編碼沒有BOM的話(顯然,你不能保證用戶給你的所有Unicode檔案都是有BOM的),那麼你要手動從原始位元組中判斷他是GBK?還是UTF8?還是其他編碼? 。這就需要具體的編碼覺察演算法了(可以google “charset|encoding detection”), 當然編碼覺察演算法不一定會100%準確,正是因為這點,Windows記事本會有Bush hid the facts bug。在Chrome瀏覽網頁時,也會遇到亂碼的狀況的。個人感覺,Notepad++的編碼覺察做的還是很準確的。
編碼覺察演算法很多,例如這個工程:https://code.google.com/p/ude
如果Unicode都帶BOM的話,則不需要第三方類別庫了。不過也有一些需要說明的地方。
問題就是.NET中讀取文字方法(File類別和StreamReader)預設是以UTF8編碼來讀取的,因此此類GBK的文字檔案直接用.NET開啟(不指定編碼的話)結果肯定是亂碼!
首先這裡最有效地解決方案是使用系統預設的ANSI擴展編碼,也就是系統預設的非Unicode編碼來讀取文本,參考代碼:
//输出系统默认非Unicode编码Console.WriteLine(Encoding.Default.EncodingName);//使用系统默认非Unicode编码来打开文件var fileContent = File.ReadAllText("C:\test.txt", Encoding.Default);
在簡體中文的Windows系統下應該輸出:
簡體中文(GB2312)<文字內容省略>...
而且使用這個方法其實是不限於簡體中文的。
當然也可以手動去指定一個編碼,例如就是GBK編碼,但是如果用指定的GBK編碼去打開一個Unicode文件,文件還會打開成功嗎?答案是仍然成功。原因是.NET在開啟文件時預設會自動覺察BOM然後用根據BOM得到的編碼去開啟文件,如果沒有BOM再用使用者指定的編碼區開啟文件,如果使用者沒有指定編碼,則使用UTF8編碼。
這個」自動覺察BOM「的參數可以在StreamReader中建構函式中設置,對應detectEncodingFromByteOrderMarks參數。
但是在File類別的對應方法中無法設定。 (例如:File.ReadAllText)。
例如下面程式碼,分別用:
GB2312編碼,自動覺察BOM 來讀取GB2312文字
GB2312編碼,自動覺察BOM 來讀取Unicode文字
GB2312編碼,不覺察BOM 來讀取Unicode文字
static void Main(){ var gb2312 = Encoding.GetEncoding("GB2312"); //用GB2312编码,自动觉察BOM 来读取GB2312文本 ReadFile("gbk.txt", gb2312, true); //用GB2312编码,自动觉察BOM 来读取Unicode文本 ReadFile("unicode.txt", gb2312, true); //用GB2312编码,不觉察BOM 来读取Unicode文本 ReadFile("unicode.txt", gb2312, false);}//通过StreamReader读取文本 static void ReadFile(string path, Encoding enc, bool detectEncodingFromByteOrderMarks){ StreamReader sr; using (sr = new StreamReader(path, enc, detectEncodingFromByteOrderMarks)) { Console.WriteLine(sr.ReadToEnd()); }}
輸出:
a刘a刘???
第三行是亂碼。
看到上面,使用GB2312編碼去開啟Unicode檔案也會成功的。因為「自動覺察BOM」參數為True,所以當發現該文件有BOM,.NET會透過BOM覺察到是Unicode文件,然後用Unicode去開啟文件的。當然如果沒有BOM,會使用指定的編碼參數去開啟檔案。對於GB2312編碼的文本,顯然是沒有BOM的,所以必須指定GB2312編碼,否則.NET會用預設的UTF8編碼去解析文件,是無法讀取結果的。第三行出現亂碼則是由於「自動覺察BOM」為False,.NET會直接用指定的GB2312編碼去讀取一個有BOM的Unicode編碼文字文件,顯然無法成功的。
當然還可以自己判斷BOM,如果沒有BOM的話,指定一個預設編碼去開啟文字。我在以前一篇文章中寫過(.NET(C#):從文件中覺察編碼)。
代碼:
static void Main(){ PrintText("gb2312.txt"); PrintText("unicode.txt");}//根据文件自动觉察编码并输出内容static void PrintText(string path){ var enc = GetEncoding(path, Encoding.GetEncoding("GB2312")); using (var sr = new StreamReader(path, enc)) { Console.WriteLine(sr.ReadToEnd()); }}/// <summary>/// 根据文件尝试返回字符编码/// </summary>/// <param name="file">文件路径</param>/// <param name="defEnc">没有BOM返回的默认编码</param>/// <returns>如果文件无法读取,返回null。否则,返回根据BOM判断的编码或者缺省编码(没有BOM)。</returns>static Encoding GetEncoding(string file, Encoding defEnc){ using (var stream = File.OpenRead(file)) { //判断流可读? if (!stream.CanRead) return null; //字节数组存储BOM var bom = new byte[4]; //实际读入的长度 int readc; readc = stream.Read(bom, 0, 4); if (readc >= 2) { if (readc >= 4) { //UTF32,Big-Endian if (CheckBytes(bom, 4, 0x00, 0x00, 0xFE, 0xFF)) return new UTF32Encoding(true, true); //UTF32,Little-Endian if (CheckBytes(bom, 4, 0xFF, 0xFE, 0x00, 0x00)) return new UTF32Encoding(false, true); } //UTF8 if (readc >= 3 && CheckBytes(bom, 3, 0xEF, 0xBB, 0xBF)) return new UTF8Encoding(true); //UTF16,Big-Endian if (CheckBytes(bom, 2, 0xFE, 0xFF)) return new UnicodeEncoding(true, true); //UTF16,Little-Endian if (CheckBytes(bom, 2, 0xFF, 0xFE)) return new UnicodeEncoding(false, true); } return defEnc; }}//辅助函数,判断字节中的值static bool CheckBytes(byte[] bytes, int count, params int[] values){ for (int i = 0; i < count; i++) if (bytes[i] != values[i]) return false; return true;}
上面程式碼,對於Unicode文本,GetEncoding方法會傳回UTF16編碼(更具體:還會根據BOM傳回Big或Little-Endian的UTF16編碼),而沒有BOM的檔案則會傳回預設值GB2312編碼。
Related Posts:
.NET(C#):從檔案中覺察編碼
.NET(C#):字元編碼(Encoding)和位元組順序標記(BOM )
.NET(C#):使用System.Text.Decoder類別來處理「流程文字」
.NET(C#):淺談組件清單資源與RESX資源
以上是關於.NET(C#)正確讀取中文編碼檔案的實例教程的詳細內容。更多資訊請關注PHP中文網其他相關文章!