如何检测文本文件的字符编码?

Céd*_*vin 73 c# encoding byte-order-mark character-encoding

我尝试检测我的文件中使用了哪个字符编码.

我尝试使用此代码来获取标准编码

public static Encoding GetFileEncoding(string srcFile)
    {
      // *** Use Default of Encoding.Default (Ansi CodePage)
      Encoding enc = Encoding.Default;

      // *** Detect byte order mark if any - otherwise assume default
      byte[] buffer = new byte[5];
      FileStream file = new FileStream(srcFile, FileMode.Open);
      file.Read(buffer, 0, 5);
      file.Close();

      if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
        enc = Encoding.UTF8;
      else if (buffer[0] == 0xfe && buffer[1] == 0xff)
        enc = Encoding.Unicode;
      else if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
        enc = Encoding.UTF32;
      else if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
        enc = Encoding.UTF7;
      else if (buffer[0] == 0xFE && buffer[1] == 0xFF)      
        // 1201 unicodeFFFE Unicode (Big-Endian)
        enc = Encoding.GetEncoding(1201);      
      else if (buffer[0] == 0xFF && buffer[1] == 0xFE)      
        // 1200 utf-16 Unicode
        enc = Encoding.GetEncoding(1200);


      return enc;
    }
Run Code Online (Sandbox Code Playgroud)

我的第五个字节是60,118,56,46和49.

是否有图表显示哪些编码与这五个第一个字节匹配?

dan*_*n04 81

您不能依赖具有BOM的文件.UTF-8不要求它.而非Unicode编码甚至没有BOM.但是,还有其他方法可以检测编码.

UTF-32

BOM为00 00 FE FF(对于BE)或FF FE 00 00(对于LE).

但即使没有BOM,UTF-32也很容易检测到.这是因为Unicode代码点范围限制为U + 10FFFF,因此UTF-32单元的模式总是为00 {00-10} xx xx(对于BE)或xx xx {00-10} 00(对于LE) .如果数据的长度是4的倍数,并且遵循其中一种模式,则可以安全地假设它是UTF-32.由于面向字节编码中的00字节很少,因此误报几乎是不可能的.

US-ASCII

没有BOM,但您不需要.在80-FF范围内缺少字节可以很容易地识别ASCII.

UTF-8

BOM是EF BB BF.但你不能依赖于此.许多UTF-8文件没有BOM,特别是如果它们来自非Windows系统.

但您可以放心地假设,如果文件验证为UTF-8,则为 UTF-8.误报很少见.

具体而言,假设数据不是ASCII,则2字节序列的误报率仅为3.9%(1920/49152).对于7字节序列,它小于1%.对于12字节序列,它小于0.1%.对于一个24字节的序列,它不到百万分之一.

UTF-16

BOM为FE FF(BE)或FF FE(LE).请注意,UTF-16LE BOM位于UTF-32LE BOM的开头,因此请先检查UTF-32.

如果您碰巧有一个主要由ISO-8859-1字符组成的文件,那么该文件的一半字节为00也将是UTF-16的强指示符.

否则,在没有BOM的情况下识别UTF-16的唯一可靠方法是寻找代理对(D [8-B] xx D [CF] xx),但非BMP字符太少用于使这种方法变得实用.

XML

如果您的文件以字节3C 3F 78 6D 6C开头(即ASCII字符"<?xml"),则查找encoding=声明.如果存在,则使用该编码.如果不存在,则假设UTF-8,这是默认的XML编码.

如果您需要支持EBCDIC,还要查找等效序列4C 6F A7 94 93.

通常,如果您的文件格式包含编码声明,那么请查找该声明,而不是尝试猜测编码.

以上都不是

还有数百种其他编码,需要更多的努力来检测.我建议尝试Mozilla的字符集检测器它的.NET端口.

合理的默认值

如果您排除了UTF编码,并且没有指向不同编码的编码声明或统计检测,则假设ISO-8859-1或密切相关的Windows-1252.(请注意,最新的HTML标准要求将"ISO-8859-1"声明解释为Windows-1252.)作为Windows的英语默认代码页(以及其他流行语言,如西班牙语,葡萄牙语,德语和法语),它是UTF-8以外最常遇到的编码.

  • 对于UTF-16BE文本文件,如果某个百分比的偶数字节被清零(或检查UTF-16LE的奇数字节),则编码为UTF-16的可能性很大.你怎么看? (2认同)
  • @marsze这不是_my_回答......并没有提到它,因为这是关于_detection,_并且,正如我所提到的,你无法真正检测到简单的每字符符号编码.我个人[发布了一个答案](/sf/answers/3392219421/)在这个地方(模糊地)识别它. (2认同)
  • @marsze:在那里,我为Latin-1添加了一个部分. (2认同)

Tao*_*Tao 9

如果你想要一个"简单"的解决方案,你可能会发现我把这个课程放在一起很有用:

http://www.architectshack.com/TextFileEncodingDetector.ashx

它首先自动进行BOM检测,然后尝试区分没有BOM的Unicode编码和一些其他默认编码(通常是Windows-1252,在.Net中错误地标记为Encoding.ASCII).

如上所述,涉及NCharDet或MLang的"更重"的解决方案可能更合适,正如我在本课程的概述页面上所述,最好的是尽可能提供与用户的某种形式的交互,因为那里只是是不是100%的检测率!


Elm*_*mue 6

这里有几个答案,但没有人发布有用的代码。

这是我的代码,用于检测 Microsoft 在 StreamReader 类中的 Framework 4 中检测到的所有编码。

显然,您必须在打开流后立即调用此函数,然后才能从流中读取任何其他内容,因为 BOM 是流中的第一个字节。

此函数需要一个可以查找的 Stream(例如 FileStream)。如果您有一个无法搜索的 Stream,您必须编写一个更复杂的代码,该代码返回一个 Byte 缓冲区,其中包含已读取但不是 BOM 的字节。

/// <summary>
/// UTF8    : EF BB BF
/// UTF16 BE: FE FF
/// UTF16 LE: FF FE
/// UTF32 BE: 00 00 FE FF
/// UTF32 LE: FF FE 00 00
/// </summary>
public static Encoding DetectEncoding(Stream i_Stream)
{
    if (!i_Stream.CanSeek || !i_Stream.CanRead)
        throw new Exception("DetectEncoding() requires a seekable and readable Stream");

    // Try to read 4 bytes. If the stream is shorter, less bytes will be read.
    Byte[] u8_Buf = new Byte[4];
    int s32_Count = i_Stream.Read(u8_Buf, 0, 4);
    if (s32_Count >= 2)
    {
        if (u8_Buf[0] == 0xFE && u8_Buf[1] == 0xFF)
        {
            i_Stream.Position = 2;
            return new UnicodeEncoding(true, true);
        }

        if (u8_Buf[0] == 0xFF && u8_Buf[1] == 0xFE)
        {
            if (s32_Count >= 4 && u8_Buf[2] == 0 && u8_Buf[3] == 0)
            {
                i_Stream.Position = 4;
                return new UTF32Encoding(false, true);
            }
            else
            {
                i_Stream.Position = 2;
                return new UnicodeEncoding(false, true);
            }
        }

        if (s32_Count >= 3 && u8_Buf[0] == 0xEF && u8_Buf[1] == 0xBB && u8_Buf[2] == 0xBF)
        {
            i_Stream.Position = 3;
            return Encoding.UTF8;
        }

        if (s32_Count >= 4 && u8_Buf[0] == 0 && u8_Buf[1] == 0 && u8_Buf[2] == 0xFE && u8_Buf[3] == 0xFF)
        {
            i_Stream.Position = 4;
            return new UTF32Encoding(true, true);
        }
    }

    i_Stream.Position = 0;
    return Encoding.Default;
}
Run Code Online (Sandbox Code Playgroud)


Phi*_*unt 5

使用StreamReader并指示它为您检测编码:

using (var reader = new System.IO.StreamReader(path, true))
{
    var currentEncoding = reader.CurrentEncoding;
}
Run Code Online (Sandbox Code Playgroud)

并使用代码页标识符 https://msdn.microsoft.com/en-us/library/windows/desktop/dd317756(v=vs.85).aspx ,以便根据它来切换逻辑.

  • 此版本仅检查BOM (4认同)
  • 不行,StreamReader假设您的文件是UTF-8 (3认同)
  • 嗯,你不必在阅读`CurrentEncoding`之前调用`Read()`吗?[MSDN for CurrentEncoding](http://msdn.microsoft.com/en-us/library/system.io.streamreader.currentencoding.aspx)说"在第一次调用StreamReader的任何Read方法后,该值可能会有所不同因为在第一次调用Read方法之前不会进行编码自动检测." (2认同)