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

devtools/2024/10/4 15:07:29/

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

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

前言

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

Unicode 称为 Unicode 16 LE
BigEndianUnicode 称为 Unicode 16 BE
Unicode32 称为 Unicode 32 LE
Unicode32BE 称为 Unicode 32 BE

一、有 BOM 文件头

文件编码文件头 0-3 字节顺序 BOM 标记汉字字节组成以“中文”为例的编码(尾含回车)
Unicode0xFF,0xFE,必非0,视中英文定一个汉字 2 字节,“中文”二字共 4 字节2D 4E 87 65 0D 00 0A 00
BigEndianUnicode0xFE,0xFF,视中英文定,必非0一个汉字 2 字节,“中文”二字共 4 字节4E 2D 65 87 00 0D 00 0A
UTF320xFF,0xFE,0,0一个汉字 4 字节,“中文”二字共 8 字节2D 4E 00 00 87 65 00 00 0D 00 00 00 0A 00 00 00
UTF32BE0,0,0xFE,0xFF一个汉字 4 字节,“中文”二字共 8 字节00 00 4E 2D 00 00 65 87 00 00 00 0D 00 00 00 0A
UTF80xEF,0xBB,0xBF一个汉字3字节,“中文”二字共6字节E4 B8 AD E6 96 87 0D 0A

汉字混搭对比示例说明:

文件编码字符串十六进制
Unicode 16 LELOVE C#\r\n中文FF FE 4C 00 4F 00 56 00 45 00 20 00 43 00 23 00 0D 00 0A 00 2D 4E 87 65
Unicode 16 BELOVE C#\r\n中文FE FF 00 4C 00 4F 00 56 00 45 00 20 00 43 00 23 00 0D 00 0A 4E 2D 65 87
Unicode 32 LELOVE C#\r\n中文FF FE 00 00 4C 00 00 00 4F 00 00 00 56 00 00 00 45 00 00 00 20 00 00 00
43 00 00 00 23 00 00 00 0D 00 00 00 0A 00 00 00 2D 4E 00 00 87 65 00 00
Unicode 32 BELOVE C#\r\n中文00 00 FE FF 00 00 00 4C 00 00 00 4F 00 00 00 56 00 00 00 45 00 00 00 20
00 00 00 43 00 00 00 23 00 00 00 0D 00 00 00 0A 00 00 4E 2D 00 00 65 87
UTF8LOVE C#\r\n中文EF BB BF 4C 4F 56 45 20 43 23 0D 0A E4 B8 AD E6 96 87

从上面有 BOM 头的编码可以看出:
Unicode 16 LE 与 Unicode 16 BE 的区别是每 2 个字节反序。
Unicode 32 LE 与 Unicode 32 BE 的区别是每 4 个字节反序。
Unicode 16 英语字符区间内,一个英文字母为单字节,占用一个 00 字节;Unicode 32 占用空间更大,一个英文字母占用 4 个 00 字节;UTF8文件越大占用空间越小。几乎没有 00 字节。

二、无 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 0D 00 00 00 0A 00 00 00
UTF32BE一个汉字 4 字节,“中文”二字共 8 字节00 00 4E 2D 00 00 65 87 00 00 00 0D 00 00 00 0A
UTF8一个汉字3字节,“中文”二字共6字节E4 B8 AD E6 96 87 0D 0A
ANSI一个汉字 2 字节,“中文”二字共 4 字节D6 D0 CE C4 0D 0A

汉字混搭对比示例说明:

文件编码字符串十六进制(无BOM头部字节识别码,可从字节排序识别)
Unicode 16 LELOVE C#\r\n中文4C 00 4F 00 56 00 45 00 20 00 43 00 23 00 0D 00 0A 00 2D 4E 87 65
Unicode 16 BELOVE C#\r\n中文00 4C 00 4F 00 56 00 45 00 20 00 43 00 23 00 0D 00 0A 4E 2D 65 87
Unicode 32 LELOVE C#\r\n中文4C 00 00 00 4F 00 00 00 56 00 00 00 45 00 00 00 20 00 00 00 43 00 00 00
23 00 00 00 0D 00 00 00 0A 00 00 00 2D 4E 00 00 87 65 00 00
Unicode 32 BELOVE C#\r\n中文00 00 00 4C 00 00 00 4F 00 00 00 56 00 00 00 45 00 00 00 20 00 00 00 43
00 00 00 23 00 00 00 0D 00 00 00 0A 00 00 4E 2D 00 00 65 87
UTF8LOVE C#\r\n中文4C 4F 56 45 20 43 23 0D 0A E4 B8 AD E6 96 87

从上面没有有 BOM 头的编码可以看出,与有 BOM 头的编码相比,只是少了文件头的 BOM 标识。

三、简体中文汉字编码

中文编码(按新旧顺序)代码页汉字字符串以“中文”为例的编码
Unicode 321200中文2D 4E 87 65
Unicode 32 BE1201中文4E 2D 65 87
Unicode1200中文2D 4E 00 00 87 65 00 00
Unicode BE1201中文00 00 4E 2D 00 00 65 87
UTF865001中文E4 B8 AD E6 96 87
GB18030CP54936中文D6 D0 CE C4
GBKCP936中文D6 D0 CE C4
GB2312CP20936中文D6 D0 CE C4
HZ-GB2312CP52936中文D6 D0 CE C4
BIG5CP950中文A4 A4 A4 E5

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

//声明引用指令名称空间
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

1、文件选择按钮代码:

    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 的文件识别

       /// <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标记的UTF16if (encoding == null)//不是无BOM标记的UTF16{if (IsOnlyAscii(bytes))//只有ASCII字符{return Encoding.ASCII;//纯 ASCII }if(IsUTF8Bytes(bytes))识别无BOM标记的UTF8{return Encoding.UTF8; //无BOM标记的UTF8}else //排除所有 UTF16、UTF8、纯ASCII,剩下的就是中文状态的文本文件{if (IsBIG5(bytes)){return Encoding.GetEncoding("BIG5");//一代繁体编码}              else if (IsGB2312(bytes))//一代简体中文编码{return Encoding.GetEncoding("GB2312");}else if (IsGBK(bytes))//二代中文编码(包含简体、繁体中文){return Encoding.GetEncoding("GBK");}else if (IsGB18030(bytes))//三代中文编码,四代中文编码为全球通 UTF 码中文{return Encoding.GetEncoding("GB18030");}else{return Encoding.Default;//无法识别,设定默认为系统编码}}else{return encoding;//无BOM标记的UTF16}}}

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

 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 此时第一位必须为1if ((curByte & 0xC0) != 0x80){return false;}charByteCounter--;}}if (charByteCounter > 1){throw new Exception("非预期的byte格式");}return true;}

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

 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 和中文编码

        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;}static bool IsBIG5(byte[] bytes){string input = Encoding.GetEncoding("BIG5").GetString(bytes);var regex = new Regex("[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|[\xa1-\xfe])");//检查是否都匹配BIG5编码区间return regex.IsMatch(input);}private static bool IsGBK(byte[] bytes){string input = Encoding.GetEncoding("GBK").GetString(bytes);// 正则表达式匹配所有汉字字符var regex = new Regex(@"[\u4E00-\u9FA5]"); // 检查输入字符串中的所有字符是否都匹配GBK编码区间return regex.IsMatch(input);}private static bool IsGB2312(byte[] bytes){string input = Encoding.GetEncoding("GB2312").GetString(bytes);var regex = new Regex(@"[\uB0A1-\uF7FE\u8140-\uA4D0]"); // 检查输入字符串中的所有字符是否都匹配GB2312编码区间return regex.IsMatch(input);}private static bool IsGB18030(byte[] bytes){string input = Encoding.GetEncoding("GB18030").GetString(bytes);var regex = new Regex(@"[\u4E00-\u9FA5\uE7C7-\uE7F3]");// 检查输入字符串中的所有字符是否都匹配GB18030编码区间return regex.IsMatch(input);}

五、获得各种编码的汉字

            Console.WriteLine ("GB2312汉字区 B0-F7,A1-FE");for (int i = 176; i < 248; i++)//GB2312汉字区,首字节 B0-F7, 第二字节 A1-FE{for (int j = 161; j < 255; j++){Console.Write("{0},{1},{2},{3} ", i, j,i.ToString("X")+ j.ToString("X"), GB18030.GetString(new byte[] { (byte)i, (byte)j }).ToString());}Console.Write("\r\n");}Console.WriteLine("");Console.WriteLine("CJK3汉字区 81-A0,40-FE");for (int i = 129; i < 161; i++)//CJK3汉字区 81-A0,40-FE{for (int j = 64; j < 255; j++){Console.Write("{0},{1},{2},{3} ", i, j, i.ToString("X") + j.ToString("X"), GB18030.GetString(new byte[] { (byte)i, (byte)j }).ToString());}Console.Write("\r\n");}Console.WriteLine("");Console.WriteLine("CJK4汉字区 AA-FE,40-FE");for (int i = 170; i < 255; i++)//CJK4汉字区 AA-FE,40-FE{for (int j = 64; j < 161; j++){Console.Write("{0},{1},{2},{3} ", i, j, i.ToString("X") + j.ToString("X"), GB18030.GetString(new byte[] { (byte)i, (byte)j }).ToString());}Console.Write("\r\n");}Console.WriteLine("");Console.WriteLine("BIG5基本汉字区 A4-C6,40-7E");for (int i = 164; i < 199; i++)//BIG5汉字区 A4-C6,40-7E{for (int j = 64; j < 126; j++){Console.Write("{0},{1},{2},{3} ", i, j, i.ToString("X") + j.ToString("X"), BIG5.GetString(new byte[] { (byte)i, (byte)j }).ToString());}Console.Write("\r\n");}Console.WriteLine("");Console.WriteLine("BIG5补充汉字区 C9-F9,40-D5");for (int i = 201; i < 250; i++)//BIG5汉字区 C9-F9,40-D5{for (int j = 64; j < 214; j++){Console.Write("{0},{1},{2},{3} ", i, j, i.ToString("X") + j.ToString("X"), BIG5.GetString(new byte[] { (byte)i, (byte)j }).ToString());}Console.Write("\r\n");}

六、获得各种编码的代码页和名称

            using (StreamWriter writer = new StreamWriter(Application.StartupPath + "\\EncodingCodePage.TXT", false, Encoding.Default)){EncodingInfo[] encodings = Encoding.GetEncodings();writer.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", "编码代码页", "编码名称", "编码头名称", "编码WEB名称", "Windows系统编码代码页");foreach (var encodingInfo in encodings){Encoding encoding = Encoding.GetEncoding(encodingInfo.CodePage);writer.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", encodingInfo.CodePage, encodingInfo.Name, encoding.HeaderName, encoding.WebName, encoding.WindowsCodePage);}}

七、程序通过验证

在这里插入图片描述

八、源代码下载

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


http://www.ppmy.cn/devtools/121316.html

相关文章

滚雪球学MySQL[5.2讲]:并发事务的处理

全文目录&#xff1a; 前言5.2 并发事务的处理1. 锁机制详解1.1 行锁&#xff08;Row Locks&#xff09;行锁的工作机制示例1&#xff1a;共享锁与排他锁 1.2 表锁&#xff08;Table Locks&#xff09;表锁的工作机制示例2&#xff1a;表锁的使用 1.3 行锁与表锁的对比 2. 死锁…

Another redis desktop manager使用说明

Another redis desktop manager使用说明 概述界面介绍图示说明连接界面设置界面查看操作日志主界面信息进入redis-cli控制台更多 概述 Another Redis Desktop Manager是一个开源的跨平台 Redis 客户端&#xff0c;提供了简洁易用的图形用户界面&#xff08;GUI&#xff09;&am…

加密与安全_TOTP 一次性密码生成算法

文章目录 PreTOTP是什么TOTP 算法工作原理TOTP 生成公式TOTP 与 HOTP 的对比Code生成TOTP验证 TOTP使用场景小结 TOTP 与 HOTP 的主要区别TOTP 与 HOTP应用场景比较TOTP 与 HOTP安全性分析 Pre 加密与安全_HTOP 一次性密码生成算法 https://github.com/samdjstevens/java-tot…

Docker Compose 部署大模型GPU集群:高效分配与管理算力资源

Docker Compose 部署大模型GPU集群&#xff1a;高效分配与管理算力资源 文章目录 Docker Compose 部署大模型GPU集群&#xff1a;高效分配与管理算力资源一 Dockerfile 编写二 Dockerfile 示例三 分配GPU资源1&#xff09;GPU分配&#xff1a;指定count2&#xff09;GPU分配&am…

科技赋能,商贸物流新速度 —— 智慧供应链商城加速企业成长

科技赋能&#xff0c;商贸物流新速度 —— 智慧供应链商城加速企业成长 随着科技的飞速发展&#xff0c;AI&#xff08;人工智能&#xff09;、大数据、物联网等先进技术正深刻重塑着商贸物流行业&#xff0c;推动其向更高效、更智能、更环保的方向迈进。这些技术的应用不仅提…

[数据结构] 二叉树题目 (二)

目录 一. 另一颗树的子树 1.1 题目 1.2 示例 1.3 分析 1.4 解决 二. 平衡二叉树 2.1 题目 2.2 示例 2.3 分析 2.4 解决 三. 二叉树的遍历和创建 3.1 题目 3.2 示例 3.3 解决 一. 另一颗树的子树572. 另一棵树的子树 - 力扣&#xff08;LeetCode&#xff09; 1.1…

硬件-开关电源-结构组成及元件作用

文章目录 一&#xff1a;开关电源组成1.1 开关电源是什么&#xff1f;1.2 开关电源六个组成部分 二&#xff1a;六个组成部分的作用2.1 EMC区域2.2 输入整流滤波区域2.3 控制区域2.4 变压器2.5 输出整流滤波区域2.6 反馈电路区域道友:勿以小恶弃人大美&#xff0c;勿以小怨忘人…

2-2.Jetpack 之 Room 简单编码模板(优化版)(Entity、DAO、Database、Repository)

一、Room 1、Room 概述 Room 是 Jetpack 中的一个重要成员&#xff0c;它是一个持久化库&#xff0c;它为管理数据库提供了简单强大的方法 2、Room 引入 在模块级 build.gradle 中引入相关依赖 implementation "androidx.room:room-runtime:2.2.5" annotationPr…