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

时间:2010-12-23 15:40:16

标签: c# encoding character-encoding byte-order-mark

我尝试检测文件中使用的字符编码。

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

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;
    }

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

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

8 个答案:

答案 0 :(得分:78)

您不能依赖具有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's charset detectora .NET port of it

合理的默认值

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

答案 1 :(得分:9)

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

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

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

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

答案 2 :(得分:5)

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

using (var reader = new System.IO.StreamReader(path, true))
{
    var currentEncoding = reader.CurrentEncoding;
}

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

答案 3 :(得分:4)

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

这是我的代码,它检测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;
}

答案 4 :(得分:2)

答案 5 :(得分:2)

答案 6 :(得分:1)

如果您的文件以字节60,118,56,46和49开头,则您的案例不明确。它可以是UTF-8(无BOM)或任何单字节编码,如ASCII,ANSI,ISO-8859-1等。

答案 7 :(得分:1)

我使用的是Ude,它是Mozilla Universal Charset Detector的C#端口。它易于使用,并提供了一些非常好的结果。