惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
SegmentFault 最新的问题
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Attack and Defense Labs
Attack and Defense Labs
F
Full Disclosure
Vercel News
Vercel News
N
News | PayPal Newsroom
The GitHub Blog
The GitHub Blog
H
Hacker News: Front Page
H
Heimdal Security Blog
P
Privacy International News Feed
博客园 - 司徒正美
Google DeepMind News
Google DeepMind News
N
Netflix TechBlog - Medium
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cisco Blogs
L
Lohrmann on Cybersecurity
D
Docker
Recent Announcements
Recent Announcements
Security Archives - TechRepublic
Security Archives - TechRepublic
人人都是产品经理
人人都是产品经理
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Proofpoint News Feed
T
Tailwind CSS Blog
C
Check Point Blog
博客园 - 叶小钗
Google Online Security Blog
Google Online Security Blog
Martin Fowler
Martin Fowler
Stack Overflow Blog
Stack Overflow Blog
博客园 - 聂微东
S
Secure Thoughts
博客园 - Franky
博客园_首页
阮一峰的网络日志
阮一峰的网络日志
P
Palo Alto Networks Blog
Latest news
Latest news
量子位
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 三生石上(FineUI控件)
The Cloudflare Blog
Last Week in AI
Last Week in AI
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cyberwarzone
Cyberwarzone
小众软件
小众软件
Cisco Talos Blog
Cisco Talos Blog
Hacker News: Ask HN
Hacker News: Ask HN
T
Threatpost
T
Tenable Blog
P
Privacy & Cybersecurity Law Blog
WordPress大学
WordPress大学

博客园 - 雪域月光

windows如何查看cpu是否支持aux2指令集 git commit错误 error: bad signature 0x00000000 fatal: index file corrupt git pull时遇到error: cannot lock ref 'xxx': ref xxx is at (一个commitID) but expected的解决办法 git tortoise commit时出错 fatal : unable to read f77cxxxxxxxxxxx git unable to resolve reference 'refs/remotes/gitee/20210825-add-Projet-coreScene': reference broken git如何获取远端仓库新分支 git 清除本地修改 项目添加git忽略文件无效的处理 git tortoise commit 出现 fatal bad revidion head freeswitch 使用info显示的通道变量 freeswitch dialplan 基础 字符编码:Unicode和UTF-8之间的关系 UTF-8和GBK有什么区别 gb2312,gbk,utf8的区别 php+javascript实现的动态显示服务器运行程序进度条功能示例 PHP的ob_flush()与flush()区别 微信小程序如何实现和微信客服通话? 微信小程序如何接入? 微信小程序和微信公众号的id是一个吗
NET(C#):关于正确读取中文编码文件
雪域月光 · 2018-08-07 · via 博客园 - 雪域月光

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编码来读取文本,参考代码:

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

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

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

当然也可以手动去指定一个编码,比如就是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());
    }
}

输出:

第三行是乱码。

看到上面,使用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:

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