https://blog.csdn.net/ma_jiang/article/details/53213442

首先如果读者对编码或者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编码来读取文本,参考代码:

  1. //输出系统默认非Unicode编码
  2. Console.WriteLine(Encoding.Default.EncodingName);
  3. //使用系统默认非Unicode编码来打开文件
  4. var fileContent = File.ReadAllText("C:\test.txt", Encoding.Default);

在简体中文的Windows系统下应该输出:

  1. 简体中文(GB2312)
  2. <文本内容省略>...

而且使用这个方法其实是不限于简体中文的。

当然也可以手动去指定一个编码,比如就是GBK编码,但是如果用指定的GBK编码去打开一个Unicode文件,文件还会打开成功吗?答案是仍然成功。原因是.NET在打开文件时默认会自动觉察BOM然后用根据BOM得到的编码去打开文件,如果没有BOM再用用户指定的编码区打开文件,如果用户没有指定编码,则使用UTF8编码。

这个”自动觉察BOM“的参数可以在StreamReader中构造函数中设置,对应detectEncodingFromByteOrderMarks参数。

但是在File类的相应方法中无法设置。(比如:File.ReadAllText)。

比如下面代码,分别用:

  • GB2312编码,自动觉察BOM 来读取GB2312文本
  • GB2312编码,自动觉察BOM 来读取Unicode文本
  • GB2312编码,不觉察BOM 来读取Unicode文本
  1. static void Main()
  2. {
  3. var gb2312 = Encoding.GetEncoding("GB2312");
  4. //用GB2312编码,自动觉察BOM 来读取GB2312文本
  5. ReadFile("gbk.txt", gb2312, true);
  6. //用GB2312编码,自动觉察BOM 来读取Unicode文本
  7. ReadFile("unicode.txt", gb2312, true);
  8. //用GB2312编码,不觉察BOM 来读取Unicode文本
  9. ReadFile("unicode.txt", gb2312, false);
  10. }
  11.  
  12. //通过StreamReader读取文本
  13. static void ReadFile(string path, Encoding enc, bool detectEncodingFromByteOrderMarks)
  14. {
  15. StreamReader sr;
  16. using (sr = new StreamReader(path, enc, detectEncodingFromByteOrderMarks))
  17. {
  18. Console.WriteLine(sr.ReadToEnd());
  19. }
  20. }

输出:

  1. a
  2. a
  3. ???

第三行是乱码。

看到上面,使用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#):从文件中觉察编码)。

代码:

  1. static void Main()
  2. {
  3. PrintText("gb2312.txt");
  4. PrintText("unicode.txt");
  5. }
  6.  
  7. //根据文件自动觉察编码并输出内容
  8. static void PrintText(string path)
  9. {
  10. var enc = GetEncoding(path, Encoding.GetEncoding("GB2312"));
  11. using (var sr = new StreamReader(path, enc))
  12. {
  13. Console.WriteLine(sr.ReadToEnd());
  14. }
  15. }
  16.  
  17. /// <summary>
  18. /// 根据文件尝试返回字符编码
  19. /// </summary>
  20. /// <param name="file">文件路径</param>
  21. /// <param name="defEnc">没有BOM返回的默认编码</param>
  22. /// <returns>如果文件无法读取,返回null。否则,返回根据BOM判断的编码或者缺省编码(没有BOM)。</returns>
  23. static Encoding GetEncoding(string file, Encoding defEnc)
  24. {
  25. using (var stream = File.OpenRead(file))
  26. {
  27. //判断流可读?
  28. if (!stream.CanRead)
  29. return null;
  30. //字节数组存储BOM
  31. var bom = new byte[4];
  32. //实际读入的长度
  33. int readc;
  34.  
  35. readc = stream.Read(bom, 0, 4);
  36.  
  37. if (readc >= 2)
  38. {
  39. if (readc >= 4)
  40. {
  41. //UTF32,Big-Endian
  42. if (CheckBytes(bom, 4, 0x00, 0x00, 0xFE, 0xFF))
  43. return new UTF32Encoding(true, true);
  44. //UTF32,Little-Endian
  45. if (CheckBytes(bom, 4, 0xFF, 0xFE, 0x00, 0x00))
  46. return new UTF32Encoding(false, true);
  47. }
  48. //UTF8
  49. if (readc >= 3 && CheckBytes(bom, 3, 0xEF, 0xBB, 0xBF))
  50. return new UTF8Encoding(true);
  51.  
  52. //UTF16,Big-Endian
  53. if (CheckBytes(bom, 2, 0xFE, 0xFF))
  54. return new UnicodeEncoding(true, true);
  55. //UTF16,Little-Endian
  56. if (CheckBytes(bom, 2, 0xFF, 0xFE))
  57. return new UnicodeEncoding(false, true);
  58. }
  59.  
  60. return defEnc;
  61. }
  62. }
  63.  
  64. //辅助函数,判断字节中的值
  65. static bool CheckBytes(byte[] bytes, int count, params int[] values)
  66. {
  67. for (int i = 0; i < count; i++)
  68. if (bytes[i] != values[i])
  69. return false;
  70. return true;
  71. }

上面代码,对于Unicode文本,GetEncoding方法会返回UTF16编码(更具体:还会根据BOM返回Big或者Little-Endian的UTF16编码),而没有BOM的文件则会返回缺省值GB2312编码。

Related Posts:
  1. .NET(C#):从文件中觉察编码
  2. .NET(C#):字符编码(Encoding)和字节顺序标记(BOM)
  3. .NET(C#):使用System.Text.Decoder类来处理“流文本”
  4. .NET(C#):浅谈程序集清单资源和RESX资源

NET(C#):关于正确读取中文编码文件的更多相关文章

  1. 路径正确下,Eclipse读取txt文件仍失败

    症状:使用Eclipse读取文件时,路径输入确认正确(前提!!!),但控制台总报错: 错误类型一: Exception in thread "main" java.io.FileN ...

  2. SpringBoot项目构建成jar运行后,如何正确读取resource下的文件

    SpringBoot项目构建成jar运行后,如何正确读取resource下的文件 不管你使用的是SpringBoot 1.x还是SpringBoot2.x,在开Dev环境中使用eclipse.IEAD ...

  3. Unity3D移动平台动态读取外部文件全解析

    前言: 一直有个想法,就是把工作中遇到的坑通过自己的深挖,总结成一套相同问题的解决方案供各位同行拍砖探讨.眼瞅着2015年第一个工作日就要来到了,小匹夫也休息的差不多了,寻思着也该写点东西活动活动大脑 ...

  4. R语言学习笔记之: 论如何正确把EXCEL文件喂给R处理

    博客总目录:http://www.cnblogs.com/weibaar/p/4507801.html ---- 前言: 应用背景兼吐槽 继续延续之前每个月至少一次更新博客,归纳总结学习心得好习惯. ...

  5. .Net读取Excel文件时丢失数据的问题 (转载)

    相信很多人都试过通过OleDB读取Excel文件,这种方法效率十分高,只是有一点会让人十分头痛,就是当一列中既有混合型数据,又有纯数据时,往往容易丢失数据. 百度过后,改连接字符串 “HDR=YES; ...

  6. 关于读取txt文件中文乱码问题

    在处理文件的过程中,读取txt文件出现中文乱码.这种情况是由于编码字符不一致导致. public static string ReadFile(string path, string fileName ...

  7. C#读取Excel文件:通过OleDb连接,把excel文件作为数据源来读取

    转载于:http://developer.51cto.com/art/200908/142392.htm C#读取Excel文件可以通过直接读取和OleDb连接,把excel文件作为数据源来读取:   ...

  8. 手工创建tomcat应用,以及实现js读取本地文件内容

    手工创建tomcat应用: 1.在webapps下面新建应用目录文件夹 2.在文件夹下创建或是从其他应用中复制:META-INF,WEB-INF这两个文件夹, 其中META-INF清空里面,WEB-I ...

  9. 使用POI读取excel文件内容

    1.前言 项目中要求读取excel文件内容,并将其转化为xml格式.常见读取excel文档一般使用POI和JExcelAPI这两个工具.这里我们介绍使用POI实现读取excel文档. 2.代码实例: ...

随机推荐

  1. Android Studio Gradle依赖冲突

    版本冲突 Gradle提供了两种解决版本冲突的策略:Newest和Fail.默认策略是Newest,配置Fail模式: configurations.all { resolutionStrategy. ...

  2. Java核心-多线程-并发控制器-Exchanger交换器

    1.基本概念 Exchanger,从名字上理解就是交换.Exchanger用于在两个线程之间进行数据交换,注意也只能在两个线程之间进行数据交换. 线程会阻塞在Exchanger的exchange方法上 ...

  3. 64 位 Windows 平台开发注意要点之文件系统重定向

    Program Files 的重定向 很多开发人员都知道,在 64 位 Windows 系统上,32 位程序是无法获取得到 C:\Program Files 的完整路径的,只能获取到 C:\Progr ...

  4. vb程序安装时需要在客户端安装MSSOAP30.dll,但注册不上,请问怎么处理

    現在想打包发布,在客戶沒有這個控件時,注冊一下,主要是不想在客户机器上安装SoapToolkit30.EXE文件 ?? 推荐解决方案 如果使用 InstallShield 工具来打包,安装完MSSOA ...

  5. IDE 热部署配置

    从eclipse切换到IDE,遇到应用不能热部署问题,解决如下 1.tomcat 中server配置下面三点需要注意 2.tomcat的deployment 中 需要选择war exploded而不是 ...

  6. [问题解决]eclipse.ini 文件配置jdk版本

    想要多个JDK 和 多个eclipse在一台电脑上同时运行无需配置环境变量,只需修改eclipse.ini文件即可启动eclipse. -vm D:\javaSE1.\jdk1.\bin\javaw. ...

  7. NFS存储服务

    NFS存储服务笔记======================================================================NFS共享存储是什么: 英文名-Netwo ...

  8. python xlrd xlwt

    1.什么是xlrd模块? 2.为什么使用xlrd模块? 3.怎样使用xlrd模块? 1.什么是xlrd模块? ♦python操作excel主要用到xlrd和xlwt这两个库,即xlrd是读excel, ...

  9. HTML5 关于本地操作文件的方法

    由于传统 b/s 开发出于安全性的考虑,浏览器对于本地文件的操作权限几乎没有,用户想要操作一个文件基本都是采用先上传到服务器, 再回显给浏览器供用户编辑,裁剪等的方法,这种方式虽然可行,但其对于服务器 ...

  10. Unresolved externa Round

    [ilink32 Error] Error: Unresolved external '__stdcall Round(const double, int)' referenced from e:\工 ...