我们的项目中会包含有很多文件,但是可能我们没有注意到的,我们的文件的编码不一定是utf-8,所以可能在别人电脑运行时出现乱码。最近在做一个项目,这个项目可以把我们的文件夹里的所有文本,判断他们是什么编码,如果不是用户规定的编码,那么就告诉用户,是否要把它规范为设置的编码。

我们常用的编码有 UTF-8 和 GBK ,所以这就是我们的重点关注编码,可惜现在没有一个好的办法区别 UTF-8 和 GBK 。

如果是带 BOM 的文件,带 BOM 就是带签名,我们可以看到在 VisualStudio 的 文件-高级保存 有 UTF-8 带签名和 UTF-8 编码。

那么带签名的意思是什么,这个和历史有关,我们做出了太多编码,有时无法解析文件的编码,如我们在记事本写上联通,再次打开会是乱码的原因一样,为了让文件自己告诉是什么编码,我们就取文件的前四个 byte ,用于让文件说出自己的编码。

对带签名文件,我们可以简单得到他的编码。See:http://lindexi.oschina.io/lindexi/post/win10-uwp-%E8%AF%BB%E5%8F%96%E6%96%87%E6%9C%ACGBK%E9%94%99%E8%AF%AF/

        private static Encoding AutoEncoding(byte[] bom)
{
if (bom.Length != 4)
{
throw new ArgumentException("EncodingScrutator.AutoEncoding 参数大小不等于4");
} // Analyze the BOM if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76)
return Encoding.UTF7; //85 116 102 55 //utf7 aa 97 97 0 0
//utf7 编码 = 43 102 120 90 if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
return Encoding.UTF8; //无签名 117 116 102 56
// 130 151 160 231
if (bom[0] == 0xff && bom[1] == 0xfe)
return Encoding.Unicode; //UTF-16LE if (bom[0] == 0xfe && bom[1] == 0xff)
return Encoding.BigEndianUnicode; //UTF-16BE if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return Encoding.UTF32; return Encoding.ASCII; //如果返回ASCII可能是GBK、无签名 utf8
}

那么对于没有带签名的文件,我们如何判断?其实我找了现在很多大神的博客,他们都认为这个是没有一个可行的方法,精确判断。所以我们只能通过一个近似的方法来判断。

找了很久,发现了一个很好的算法,对于文件长度不是3的倍数,和包含有中文、ASCII字符的 GBK 编码文件,几乎不会与UTF8混淆。

我们统计属于 GBK 的 byte 个数和属于UTF8的byte个数,比较两个个数,如果countGBK 大于 countUtf8 那么编码就是 GBK,否则是 UTF8。如果相同,gg,所以我们需要一个置信度变量。

看起来我们需要好多变量,于是写一个类

  using System.IO;
using System.Text; namespace EncodingNormalior.Model
{
/// <summary>
/// 包括文件和编码
/// </summary>
public class EncodingScrutatorFile
{
/// <summary>
/// 文件
/// </summary>
public FileInfo File { set; get; } /// <summary>
/// 编码
/// </summary>
public Encoding Encoding { set; get; } /// <summary>
/// 置信度
/// 范围0-1,1表示确定,0表示不确定,注意:ASCII编码的置信度为0
/// </summary>
public double ConfidenceCount { set; get; } = 0;
}
}

那么如何统计文件中属于 GBK的 byte 个数

我们需要知道 GBK 的编码,对于一般的 ASCII 字符,使用一个 byte 和ASCII一样,如果一个文件都是 ASCII 字符,那么GBK 编码和 ASCII 的都一样,我们统计得到属于 GBK的byte个数为0。对于其他的字符,使用两个 byte 表示。

我找到了一个大神写的判断,https://gist.github.com/neesenk/956765

实际上试过了,不如使用firstByte >= 161 && firstByte <= 247 && secondByte >= 161 && secondByte <= 254判断。

那么知道了如何判断一个字符是属于GBK,那么我们可以开始写函数CountGbk

        /// <summary>
/// 统计文件属于 GBK 的 byte数
/// </summary>
/// <returns></returns>
private int CountGbk()
{
var count = 0; //存在GBK的byte
if (CountBuffer == null)
{
ReadStream();
}
var length = CountBuffer.Length; //总长度 var buffer = CountBuffer;
const char head = (char) 0x80; //小于127 通过 &head==0 for (var i = 0; i < length; i++)
{
var firstByte = buffer[i]; //第一个byte,GBK有两个
if ((firstByte & head) == 0) //如果是127以下,那么就是英文等字符,不确定是不是GBK
{
continue; //文件全部都是127以下字符,可能是Utf-8 或ASCII
}
if (i + 1 >= length) //如果是大于127,需要两字符,如果只有一个,那么文件错了,但是我也没法做什么
{
break;
}
var secondByte = buffer[i + 1]; //如果是GBK,那么添加GBK byte 2
if (firstByte >= 161 && firstByte <= 247 &&
secondByte >= 161 && secondByte <= 254)
{
count += 2;
i++;
}
}
return count;
}

统计文件中属于 utf8 的byte个数

        /// <summary>
/// 属于 UTF8 的 byte 数
/// </summary>
/// <returns></returns>
private int CountUtf8()
{
var count = 0;
if (CountBuffer == null)
{
ReadStream();
} var length = CountBuffer.Length; var buffer = CountBuffer; // new byte[length];
const char head = (char) 0x80;
//while ((n = stream.Read(buffer, 0, n)) > 0)
{
for (var i = 0; i < length; i++)
{
var temp = buffer[i];
if (temp < 128) // !(temp&head)
{
//utf8 一开始如果byte大小在 0-127 表示英文等,使用一byte
//length++; 我们记录的是和CountGBK比较
continue;
}
var tempHead = head;
var wordLength = 0; //单词长度,一个字使用多少个byte while ((temp & tempHead) != 0) //存在多少个byte
{
wordLength++;
tempHead >>= 1;
} if (wordLength <= 1)
{
//utf8最小长度为2
continue;
} wordLength--; //去掉最后一个,可以让后面的 point大于wordLength
if (wordLength + i >= length)
{
break;
}
var point = 1; //utf8的这个word 是多少 byte
//utf8在两字节和三字节的编码,除了最后一个 byte
//其他byte 大于127
//所以 除了最后一个byte,其他的byte &head >0
for (; point <= wordLength; point++)
{
var secondChar = buffer[i + point];
if ((secondChar & head) == 0)
{
break;
}
} if (point > wordLength)
{
count += wordLength + 1;
i += wordLength;
}
}
}
return count;
}

我们判断如果是不带签名的文件,判断为 UTF8 或GBK,可以使用判断属于 GBK 的 byte 多还是 UTF8 多。

                var countUtf8 = CountUtf8();
if (countUtf8 == 0)
{
encoding = Encoding.ASCII;
}
else
{
var countGbk = CountGbk();
if (countUtf8 > countGbk)
{
encoding = Encoding.UTF8;
EncodingScrutatorFile.ConfidenceCount = (double) countUtf8/(countUtf8 + countGbk);
}
else
{
encoding = Encoding.GetEncoding("GBK");
EncodingScrutatorFile.ConfidenceCount = (double) countGbk/(countUtf8 + countGbk);
}
}

下面是我看到的大神的博客,如果希望了解编码的问题,可以参见下面的博客。

我把项目开源,希望能帮到大家。

https://github.com/lindexi/EncodingNormalior

听说项目的名字拼错了,大家不要笑。

参见:http://blog.csdn.net/wwlhsgs/article/details/45641997

http://blog.csdn.net/wgw335363240/article/details/41700045

http://www.ruanyifeng.com/blog/2010/02/url_encoding.html

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

http://blog.codingnow.com/2010/06/detect_utf-8_gbk.html

https://msdn.microsoft.com/en-us/library/dd374101(VS.85).aspx

文章还发在 https://lindexi.github.io/lindexi/post/C-%E5%88%A4%E6%96%AD%E6%96%87%E4%BB%B6%E7%BC%96%E7%A0%81/ 他会更新比较快,如果遇到任何的问题,欢迎交流


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

C# 判断文件编码的更多相关文章

  1. 2018-8-10-C#-判断文件编码

    title author date CreateTime categories C# 判断文件编码 lindexi 2018-08-10 19:16:52 +0800 2018-2-13 17:23: ...

  2. C#判断文件编码——常用字法

    使用中文写文章,当篇幅超过一定程度,必然会使用到诸如:“的”.“你”.“我”这样的常用字.本类思想便是提取中文最常用的一百个字,使用中文世界常用编码(主要有GBK.GB2312.GB18030.UTF ...

  3. C# 判断文件编码

    无耐网上各种方法都有缺陷,此方法为原创,暂问发现问题.如发现请指正 public static Encoding GetFileEncodingByContent(string path) { var ...

  4. 判断文件的编码 python

    import chardet import string path1= r'C:\Users\25456\Desktop' path = path1 + r'\深度学习.txt' with open( ...

  5. MultipartFile文件编码判断

    MultipartFile文件编码判断 搜索:Java 判断文件的字符集编码 https://blog.csdn.net/top_code/article/details/8891796 但是在Mul ...

  6. 用UltraEdit判断打开文件的编码类型 用UltraEdit或notepad记事本查看文件编码格式 用UltraEdit查看当前文件编码

    用UltraEdit查看当前文件编码 想判断文件的编码类型? 用强大的UltraEdit-32软件: UltraEdit-32的状态栏可以显示文件的编码类型,详细情况如下: ANSI/ANSCI--- ...

  7. 利用js判断文件是否为utf-8编码

    常规方案 使用FileReader以utf-8格式读取文件,根据文件内容是否包含乱码字符�,来判断文件是否为utf-8. 如果存在�,即文件编码非utf-8,反之为utf-8. 代码如下: const ...

  8. C# 判断文件的真实格式

    为了防止图片木马,通过后缀判断文件的格式是不准确的.可以通过这种方式进行判断. static void Main(string[] args) { string path = @"C:\Us ...

  9. [转发]读取txt防止读到乱码--自动根据文件编码进行读取

    以下是摘抄 /// <summary> /// 获取文件的编码格式 /// </summary> public class EncodingType { /// <sum ...

随机推荐

  1. 201521123019 《Java程序设计》第11周学习总结

    1. 本章学习总结 2. 书面作业 Q1.互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) 1.1除了使用synchronized修饰方法实现互斥同步访问,还有什么办法实现互斥同步访 ...

  2. 201521123048 《java程序设计》 第11周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) 1. ...

  3. json:JSONObject包的具体使用(JSONObject-lib包是一个beans,collections,maps,java arrays和xml和JSON互相转换的包)

    1.JSONObject介绍 JSONObject-lib包是一个beans,collections,maps,java arrays和xml和JSON互相转换的包. 2.下载jar包 http:// ...

  4. 私人订制——属于你自己的Linux

    一.前言 Linux操作系统至1991年10月5日诞生以来,就其开源性和自由性得到了很多技术大牛的青睐,每个Linux爱好者都为其贡献了自己的一份力,不管是在Linux内核还是开源软件等方面,都为我们 ...

  5. shell脚本之流程控制

      shell脚本之流程控制 shell脚本之流程控制 条件语句 条件判断 循环语句for,while,until for循环 while循环 until循环 循环控制语句continue 循环控制语 ...

  6. bookStore第三篇【用户模块、购买模块、订单模块】

    用户模块 要登陆后才能购买,因此我们先写购买模块 设计实体 private String id; private String username; private String password; p ...

  7. 建一座安全的“天空城” ——揭秘腾讯WeTest如何与祖龙共同挖掘手游安全漏洞

    作者:腾讯WeTest手游安全测试团队商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处. WeTest导读 <九州天空城3D>上线至今,长期稳定在APP Store畅销排行的前 ...

  8. java围棋游戏源代码

    //李雨泽源代码,不可随意修改.//时间:2017年9月22号.//地点:北京周末约科技有限公司.//package com.bao; /*围棋*/ /*import java.awt.*; impo ...

  9. C#中的两把双刃剑:抽象类和接口

    问题出现: 这也是我在学习抽象类和接口的时候遇到的问题,从我归纳的这三个问题,不难看出这也许是我们大多数程序员遇到问题的三个阶段, 第一阶段(基础概念):就象问题1一样,这部分人首先需要扫清基础概念的 ...

  10. 我的python学习笔记一

    我的python学习笔记,快速了解python,适合有C语言基础的. http://note.youdao.com/noteshare?id=93b9750a8950c6303467cf33cb1ba ...