用System.IO.StreamReader讀取包含漢字的txt文件時,經常會讀出亂碼(StreamWriater寫文本文件也有類似的問題),原因很簡單,就是文件的編碼(encoding)和StreamReader/Writer的encoding不對應。為了解決這個問題,我寫了一個類,來取得一個文本文件的encoding,這樣我們就可以創建對應的StreamReader和StreamWrite來讀寫,保證不會出現亂碼現象。其實原理很簡單,文本編輯器(比如XP自帶的記事本)在生成文本文件時,如果編碼格式和系統默認的編碼(中文系統下默認為GB2312)不一致時,會在txt文件開頭部分添加特定的“編碼字節序標識(Encoding Bit Order Madk,簡寫為BOM)”,類似PE格式的"MZ"文件頭。這樣它在讀取時就可以根據這個BOM來確定該文本文件生成時所使用的Encoding。這個BOM我們用記事本等程序打開默認是看不到的,但是用stream按字節讀取時是可以讀到的。我的這個TxtFileEncoding類就是根據這個BOM“文件頭”來確定txt文件生成時用到的編碼的。
1 /// <summary> 2 /// 獲取文本文件的編碼方式 3 /// </summary> 4 class TxtFileEncoding 5 { 6 public TxtFileEncoding(){} 7 8 /// <summary> 9 /// 獲取文本文件的編碼方式 10 /// </summary> 11 /// <param name="fileName"> 文件名 例如:path = @"D:\test.txt"</param> 12 /// <returns>返回編碼方式</returns> 13 public static Encoding GetEncoding(string fileName ) 14 { 15 return GetEncoding(fileName, Encoding.Default); 16 } 17 18 /// <summary> 19 /// 獲取文本流的編碼方式 20 /// </summary> 21 /// <param name="fs">文本流</param> 22 /// <returns>返回系統默認的編碼方式</returns> 23 public static Encoding GetEncoding(FileStream fs) 24 { 25 //Encoding.Default 系統默認的編碼方式 26 return GetEncoding(fs, Encoding.Default); 27 } 28 29 /// <summary> 30 /// 獲取一個文本流的編碼方式 31 /// </summary> 32 /// <param name="fileName">文件名</param> 33 /// <param name="defaultEncoding">默認編碼方式。當該方法無法從文件的頭部取得有效的前導符時,將返回該編碼方式。</param> 34 /// <returns></returns> 35 public static Encoding GetEncoding(string fileName, Encoding defaultEncoding) 36 { 37 FileStream fs = File.Open(fileName,FileMode.Open); 38 Encoding targetEncoding = GetEncoding(fs, defaultEncoding); 39 fs.Close(); 40 return targetEncoding; 41 } 42 43 /// <summary> 44 /// 獲取一個文本流的編碼方式 45 /// </summary> 46 /// <param name="fs">文本流</param> 47 /// <param name="defaultEncoding">默認編碼方式。當該方法無法從文件的頭部取得有效的前導符時,將返回該編碼方式。</param> 48 /// <returns></returns> 49 public static Encoding GetEncoding(FileStream fs, Encoding defaultEncoding) 50 { 51 Encoding targetEncoding = defaultEncoding; 52 if (fs != null && fs.Length >= 2) 53 { 54 byte b1 = 0; 55 byte b2 = 0; 56 byte b3 = 0; 57 byte b4 = 0; 58 59 long oriPos = fs.Seek(0, SeekOrigin.Begin); 60 fs.Seek(0, SeekOrigin.Begin); 61 62 b1 = Convert.ToByte(fs.ReadByte()); 63 b2 = Convert.ToByte(fs.ReadByte()); 64 if (fs.Length > 2) 65 { 66 b3 = Convert.ToByte(fs.ReadByte()); 67 } 68 if (fs.Length > 3) 69 { 70 b4 = Convert.ToByte(fs.ReadByte()); 71 } 72 73 //根據文件流的前4個字節判斷Encoding 74 //Unicode {0xFF, 0xFE}; 75 //BE-Unicode {0xFE, 0xFF}; 76 //UTF8 = {0xEF, 0xBB, 0xBF}; 77 if (b1 == 0xFE && b2 == 0xFF)//UnicodeBe 78 { 79 targetEncoding = Encoding.BigEndianUnicode; 80 } 81 if (b1 == 0xFF && b2 == 0xFE && b3 != 0xFF)//Unicode 82 { 83 targetEncoding = Encoding.Unicode; 84 } 85 if (b1 == 0xEF && b2 == 0xBB && b3 == 0xBF)//UTF8 86 { 87 targetEncoding = Encoding.UTF8; 88 } 89 90 fs.Seek(0, SeekOrigin.Begin); 91 } 92 fs.Close(); 93 return targetEncoding; 94 } 95 }