用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 }