基于 C# 的文本文件的编码识别

基于 C# 的文本文件的编码识别

  • 前言
    • [一、有 BOM 文件头](#一、有 BOM 文件头)
    • [二、无 BOM 文件头](#二、无 BOM 文件头)
    • 三、简体中文汉字编码
    • [四、C# 程序对编码的识别](# 程序对编码的识别)
      • 1、文件选择按钮代码:
      • [2、获取文件编码,有 BOM 的文件识别](#2、获取文件编码,有 BOM 的文件识别)
      • [3、获取文件编码,UTF8 无 BOM 文件的识别](#3、获取文件编码,UTF8 无 BOM 文件的识别)
      • [4、获取文件编码,UTF16 无 BOM 文件的识别](#4、获取文件编码,UTF16 无 BOM 文件的识别)
      • [5、获取非 UTF8、UTF16、UTF32 文件的 ASCII 和中文编码](#5、获取非 UTF8、UTF16、UTF32 文件的 ASCII 和中文编码)
  • 五、程序通过验证
  • 六、源代码下载

前言

在 Windows 系统下,文本编码存在有无 BOM 的编码。BOM(Byte Order Mark),字节顺序标记,出现在文本文件头部,Unicode 编码标准中用于标识文件是采用哪种格式的编码。有文件 BOM 头的 Unicode 编码容易识别,无 BOM 文件头的要在文件中查找字节顺序来判断 Unicode 编码。

一、有 BOM 文件头

文件编码 文件头 0-3 字节顺序 BOM 标记 汉字字节组成 以"中文"为例的编码(后回车)
Unicode 0xFF,0xFE,非0,非0 一个汉字 2 字节,"中文"二字共 4 字节 2D 4E 87 65 0D 00 0A 00
BigEndianUnicode 0xFE,0xFF,非0,非0 一个汉字 2 字节,"中文"二字共 4 字节 4E 2D 65 87 00 0D 00 0A
UTF32 0xFF,0xFE,0,0 一个汉字 4 字节,"中文"二字共 8 字节 2D 4E 00 00 87 65 00 00 0D 00 0A 00
UTF32BE 0xFE,0xFF,0,0 一个汉字 4 字节,"中文"二字共 8 字节 4E 2D 00 00 65 87 00 00 00 0D 00 0A
UTF8 0xEF,0xBB,0xBF 一个汉字3字节,"中文"二字共6字节 E4 B8 AD E6 96 87 0D 0A

二、无 BOM 文件头

文件编码 文件头 0-4 字节顺序 BOM 标记 汉字字节组成 以"中文"为例的编码
Unicode 一个汉字 2 字节,"中文"二字共 4 字节 2D 4E 87 65 0D 00 0A 00
BigEndianUnicode 一个汉字 2 字节,"中文"二字共 4 字节 4E 2D 65 87 00 0D 00 0A
UTF32 一个汉字 4 字节,"中文"二字共 8 字节 2D 4E 00 00 87 65 00 00 00 0D 00 0A 00
UTF32BE 一个汉字 4 字节,"中文"二字共 8 字节 4E 2D 00 00 65 87 00 00 00 0D 00 0A
UTF8 一个汉字3字节,"中文"二字共6字节 E4 B8 AD E6 96 87 0D 0A
ANSI 一个汉字 2 字节,"中文"二字共 4 字节 D6 D0 CE C4 0D 0A

三、简体中文汉字编码

中文编码(按新旧顺序) 代码页 以"中文"为例
GB18030 CP54936 D6 D0 CE C4
GBK CP936 D6 D0 CE C4
GB2312 CP20936 D6 D0 CE C4
HZ-GB2312 CP52936 D6 D0 CE C4
BIG5 CP950 A4 A4 A4 E5

四、C# 程序对编码的识别

1、文件选择按钮代码:

csharp 复制代码
    private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog Openfiledialog = new OpenFileDialog();
            Openfiledialog.InitialDirectory = Application.StartupPath;
            Openfiledialog.Filter = "文本文件(*.TXT)|*.TXT|所有文件(*.*)|*.*";
            Openfiledialog.Title = "打开文件";
            Openfiledialog.Multiselect = true;
            Openfiledialog.FilterIndex = 0;

            dataGridView1.Columns.Clear();
            DataGridViewColumn col;
            DataGridViewRow row;
            DataGridViewCell cell = new DataGridViewTextBoxCell();
            string[] HeaderText = { "文件", "编码" };
            for (int i = 0; i < 2; i++)
            {
                col = new DataGridViewColumn();
                col.HeaderText = HeaderText[i];
                col.CellTemplate = cell;
                col.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;

                dataGridView1.Columns.Add(col);
                dataGridView1.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            }

            if (Openfiledialog.ShowDialog() == DialogResult.OK)
            {
                string[] FilePath = Openfiledialog.FileNames;

                foreach (string file in FilePath)
                {
                    row = new DataGridViewRow();
                    cell = new DataGridViewTextBoxCell();
                    cell.Value = file;
                    row.Cells.Add(cell);

                    cell = new DataGridViewTextBoxCell();
                    cell.Value = GetEncoding(file).WebName;
                    row.Cells.Add(cell);
                    dataGridView1.Rows.Add(row);
                }
            }
            dataGridView1.AutoResizeColumns();
        }

2、获取文件编码,有 BOM 的文件识别

csharp 复制代码
       /// <summary>获取文件的编码格式</summary>
        /// <param name="file_name">文件</param>
        /// <returns>文件的编码类型</returns>
        private static Encoding GetEncoding(string file_name)
        {
            //文件的字符集在Windows下有两种,一种是ANSI,一种Unicode。
            //对于Unicode,Windows支持了它的三种编码方式,
            //一种是小尾编码(Unicode),一种是大尾编码(BigEndianUnicode),一种是UTF - 8编码。
            if (file_name == null)
            {
                throw new ArgumentNullException(nameof(file_name));
            }

            FileStream fs = new FileStream(file_name, FileMode.Open, FileAccess.Read);
            long FsLeng = fs.Length;

            if (FsLeng < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(FsLeng));
            }

            byte[] bytes = new byte[FsLeng];

            if (fs.Length < 3)//小于BOM文件头3字节
            {
                fs.Close();
                return Encoding.ASCII;
            }

            fs.Read(bytes, 0, bytes.Length);
            fs.Close();

            if (bytes[0] == 0xFF && bytes[1] == 0xFE && (bytes[2] != 0 || bytes[3] != 0))//Unicode BOM标记
            {
                return Encoding.Unicode;
            }
            else if (bytes[0] == 0xFE && bytes[1] == 0xFF && (bytes[2] != 0 || bytes[3] != 0))//BigEndianUnicode BOM标记
            {
                return Encoding.BigEndianUnicode;
            }
            else if (bytes[0] == 0xFF && bytes[1] == 0xFE && bytes[2] == 0 && bytes[3] == 0)//UTF32 BOM标记
            {
                return Encoding.UTF32;
            }
            else if (bytes[0] == 0x00 && bytes[1] == 0x00 && bytes[2] == 0xFE && bytes[3] == 0xFF)//UTF32BE BOM标记
            {
                return Encoding.GetEncoding("utf-32BE");
            }
            else if (bytes[0] == 0xEF && bytes[1] == 0xBB && (bytes[2] == 0xBF))//UTF8 BOM标记
            {
                return Encoding.UTF8;
            }
            else//无BOM标记
            {
                Encoding encoding = CheckUtf16Ascii(bytes, bytes.Length);//识别无BOM标记的UTF16
                if (IsUTF8Bytes(bytes))
                {
                    if (encoding == null)
                    {
                        if (IsOnlyAscii(bytes))//只有ASCII字符
                        {
                            return Encoding.ASCII;//ASCII或UTF8下的ASCII
                        }
                        else
                        {
                            return Encoding.UTF8; //无BOM标记的UTF8
                        }
                    }
                    else
                    {
                        return encoding;//无BOM标记的UTF16
                    }
                }
                else
                {
                    if (encoding == null)
                    {
                        string STR = Encoding.Default.GetString(bytes);

                        if (IsOnlyAscii(bytes))
                        {
                            return Encoding.ASCII;
                        }
                        else if (IsGBK(STR))
                        {
                            return Encoding.GetEncoding("GBK");
                        }
                        else if (IsGB2312(STR))
                        {
                            return Encoding.GetEncoding("GB2312");
                        }
                        else if (IsGB18030(STR))
                        {
                            return Encoding.GetEncoding("GB18030");
                        }
                        else
                        {
                            return Encoding.Default;//无法识别,默认为GB2312
                        }

                    }
                    else
                    {
                        return encoding;//无BOM标记的UTF16
                    }

                }
            }
        }
      

3、获取文件编码,UTF8 无 BOM 文件的识别

csharp 复制代码
 private static bool IsUTF8Bytes(byte[] data)
        {
            int charByteCounter = 1;  //计算当前正分析的字符应还有的字节数
            byte curByte; //当前分析的字节.

            for (int i = 0; i < data.Length; i++)
            {
                curByte = data[i];
                if (charByteCounter == 1)
                {
                    if (curByte >= 0x80)
                    {
                        //判断当前
                        while (((curByte <<= 1) & 0x80) != 0)
                        {

                            charByteCounter++;
                        }
                        //标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X 
                        if (charByteCounter == 1 || charByteCounter > 6)
                        {
                            return false;
                        }
                    }
                }
                else
                {
                    //若是UTF-8 此时第一位必须为1
                    if ((curByte & 0xC0) != 0x80)
                    {
                        return false;
                    }
                    charByteCounter--;
                }
            }

            if (charByteCounter > 1)
            {
                throw new Exception("非预期的byte格式");
            }
            return true;
        }

4、获取文件编码,UTF16 无 BOM 文件的识别

csharp 复制代码
 private static Encoding CheckUtf16Ascii(byte[] buffer, int size)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            if (size < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(size));
            }

            if (size < 2)
            {
                return null;
            }

            // 将大小减小1,这样我们就不必担心字节对的边界检查
            size--;

            const double threshold = 0.5; // 允许一些非英语ISO-8859-1子集的UTF-16字符,同时仍检测编码。
            const double limit = 0.1;

            var leAsciiChars = 0;
            var beAsciiChars = 0;

            var pos = 0;
            while (pos < size)
            {
                byte ch1 = buffer[pos++];
                byte ch2 = buffer[pos++];

                // 偶数计数为空
                if (ch1 == 0 && ch2 != 0)
                {
                    beAsciiChars++;
                }

                // 奇数计数空值
                if (ch1 != 0 && ch2 == 0)
                {
                    leAsciiChars++;
                }
            }

            // 恢复大小
            size++;

            double leAsciiCharsPct = leAsciiChars * 2.0 / size;
            double beAsciiCharsPct = beAsciiChars * 2.0 / size;

            if (leAsciiCharsPct > threshold && beAsciiCharsPct < limit)
            {
                return Encoding.Unicode;
            }

            if (beAsciiCharsPct > threshold && leAsciiCharsPct < limit)
            {
                return Encoding.BigEndianUnicode;
            }

            // 无法识别的编码
            return null;
        }

5、获取非 UTF8、UTF16、UTF32 文件的 ASCII 和中文编码

csharp 复制代码
        private static bool IsOnlyAscii(byte[] bytes)
        {
            if (bytes == null)
            {
                throw new ArgumentNullException(nameof(bytes));
            }
            for (int i = 0; i < bytes.Length; i++)
            {
                if (bytes[i] > 127) return false;//小于127的只有ASCII字符
            }
            return true;
        }

        private static bool IsGBK(string input)
        {
            // 正则表达式匹配所有汉字字符
            var regex = new Regex(@"[\u4E00-\u9FA5]"); // 检查输入字符串中的所有字符是否都匹配GBK编码区间
            return regex.IsMatch(input);
        }

        private static bool IsGB2312(string input)
        {
            var regex = new Regex(@"[\uB0A1-\uF7FE\u8140-\uA4D0]"); // 检查输入字符串中的所有字符是否都匹配GB2312编码区间
            return regex.IsMatch(input);
        }

        private static bool IsGB18030(string input)
        {
            var regex = new Regex(@"[\u4E00-\u9FA5\uE7C7-\uE7F3]");// 检查输入字符串中的所有字符是否都匹配GB18030编码区间
            return regex.IsMatch(input);
        }

五、程序通过验证

六、源代码下载

https://download.csdn.net/download/zyyujq/89805828

相关推荐
fkdw1 小时前
C# Newtonsoft.Json 反序列化派生类数据丢失问题
c#·json
浅尝辄止;4 小时前
C# 异步编程
c#
ou.cs7 小时前
c# 实现一个简单的异常日志记录(异常迭代+分片+定时清理)+AOP Rougamo全局注入
c#
一只小小汤圆9 小时前
编译笔记:vs 中 正在从以下位置***加载符号 C# 中捕获C/C++抛出的异常
c++·c#
码农君莫笑9 小时前
Blazor项目中使用EF读写 SQLite 数据库
linux·数据库·sqlite·c#·.netcore·人机交互·visual studio
呆呆小雅10 小时前
C#关键字volatile
java·redis·c#
boligongzhu10 小时前
DALSA工业相机SDK二次开发(图像采集及保存)C#版
开发语言·c#·dalsa
web1478621072310 小时前
C# .Net Web 路由相关配置
前端·c#·.net
Jasmine_llq11 小时前
《 火星人 》
算法·青少年编程·c#