如何检测文本文件的编码/代码页

时间:2008-09-18 08:02:36

标签: c# .net text encoding globalization

在我们的应用程序中,我们会收到来自不同来源的文本文件(.txt.csv等)。在阅读时,这些文件有时会包含垃圾,因为文件是在不同的/未知的代码页中创建的。

有没有办法(自动)检测文本文件的代码页?

detectEncodingFromByteOrderMarks构造函数上的StreamReader适用于UTF8和其他unicode标记文件,但我正在寻找一种方法来检测代码页,例如{{1} },ibm850


感谢您的回答,这就是我所做的。

我们收到的文件来自最终用户,他们没有关于代码页的线索。接收者也是最终用户,现在这是他们对代码页的了解:代码页存在,并且很烦人。

解决方案:

  • 在记事本中打开收到的文件,查看一段乱码文本。如果有人被称为弗朗索瓦(François)或其他什么东西,那么凭借你的人类智慧,你可以猜到这一点。
  • 我创建了一个小应用程序,用户可以使用该应用程序打开文件,并在使用正确的代码页时输入用户知道它将出现在文件中的文本。
  • 遍历所有代码页,并显示使用用户提供的文本提供解决方案的代码页。
  • 如果弹出一个代码页,请让用户指定更多文本。

20 个答案:

答案 0 :(得分:256)

您无法检测代码页,需要告知它。您可以分析字节并猜测它,但这可能会产生一些奇怪的(有时是有趣的)结果。我现在找不到它,但我确信记事本可以用中文显示英文文本。

无论如何,这是你需要阅读的内容: The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

特别是乔尔说:

  

关于编码的最重要事实

     

如果你完全忘记了我刚才解释的一切,请记住一个非常重要的事实。在不知道它使用什么编码的情况下拥有字符串是没有意义的。你不能再把头埋在沙子里,假装“普通”文字是ASCII。   没有像纯文本那样的东西。

     

如果您有字符串,内存,文件或电子邮件消息,则必须知道它所使用的编码,或者您无法解释它或正确地将其显示给用户。

答案 1 :(得分:31)

如果您要检测非UTF编码(即无BOM),您基本上可以使用文本的启发式和统计分析。您可能需要查看Mozilla paper on universal charset detectionsame link, with better formatting via Wayback Machine)。

答案 2 :(得分:25)

您是否尝试过C# port for Mozilla Universal Charset Detector

来自http://code.google.com/p/ude/

的示例
public static void Main(String[] args)
{
    string filename = args[0];
    using (FileStream fs = File.OpenRead(filename)) {
        Ude.CharsetDetector cdet = new Ude.CharsetDetector();
        cdet.Feed(fs);
        cdet.DataEnd();
        if (cdet.Charset != null) {
            Console.WriteLine("Charset: {0}, confidence: {1}", 
                 cdet.Charset, cdet.Confidence);
        } else {
            Console.WriteLine("Detection failed.");
        }
    }
}    

答案 3 :(得分:15)

  

您无法检测代码页

这显然是错误的。每个Web浏览器都有某种通用字符集检测器来处理没有任何编码指示的页面。 Firefox有一个。您可以下载代码并查看它是如何做到的。请参阅一些文档here。基本上,它是一种启发式方法,但效果非常好。

给定合理数量的文本,甚至可以检测语言。

Here's another one我刚发现使用Google:

答案 4 :(得分:8)

我知道这个问题已经很晚了,这个解决方案不会吸引一些人(因为它以英语为中心的偏见和缺乏统计/经验测试),但是它我的工作非常好,尤其是处理上传的CSV数据:

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

优点:

  • 内置BOM检测
  • 默认/后备编码可自定义
  • 非常可靠(根据我的经验)基于西欧文件,包含一些混合了UTF-8和Latin-1风格文件的奇异数据(例如法语名称) - 基本上是美国和西欧环境的大部分

注意:我是写这个课程的人,所以显然要带上一粒盐! :)

答案 5 :(得分:7)

Notepad++具有开箱即用的此功能。它还支持更改它。

答案 6 :(得分:7)

寻找不同的解决方案,我找到了

https://code.google.com/p/ude/

这个解决方案有点沉重。

我需要一些基本的编码检测,基于4个第一个字节和可能的xml字符集检测 - 所以我从互联网上获取了一些示例源代码并添加了稍微修改过的版本

http://lists.w3.org/Archives/Public/www-validator/2002Aug/0084.html

为Java编写。

    public static Encoding DetectEncoding(byte[] fileContent)
    {
        if (fileContent == null)
            throw new ArgumentNullException();

        if (fileContent.Length < 2)
            return Encoding.ASCII;      // Default fallback

        if (fileContent[0] == 0xff
            && fileContent[1] == 0xfe
            && (fileContent.Length < 4
                || fileContent[2] != 0
                || fileContent[3] != 0
                )
            )
            return Encoding.Unicode;

        if (fileContent[0] == 0xfe
            && fileContent[1] == 0xff
            )
            return Encoding.BigEndianUnicode;

        if (fileContent.Length < 3)
            return null;

        if (fileContent[0] == 0xef && fileContent[1] == 0xbb && fileContent[2] == 0xbf)
            return Encoding.UTF8;

        if (fileContent[0] == 0x2b && fileContent[1] == 0x2f && fileContent[2] == 0x76)
            return Encoding.UTF7;

        if (fileContent.Length < 4)
            return null;

        if (fileContent[0] == 0xff && fileContent[1] == 0xfe && fileContent[2] == 0 && fileContent[3] == 0)
            return Encoding.UTF32;

        if (fileContent[0] == 0 && fileContent[1] == 0 && fileContent[2] == 0xfe && fileContent[3] == 0xff)
            return Encoding.GetEncoding(12001);

        String probe;
        int len = fileContent.Length;

        if( fileContent.Length >= 128 ) len = 128;
        probe = Encoding.ASCII.GetString(fileContent, 0, len);

        MatchCollection mc = Regex.Matches(probe, "^<\\?xml[^<>]*encoding[ \\t\\n\\r]?=[\\t\\n\\r]?['\"]([A-Za-z]([A-Za-z0-9._]|-)*)", RegexOptions.Singleline);
        // Add '[0].Groups[1].Value' to the end to test regex

        if( mc.Count == 1 && mc[0].Groups.Count >= 2 )
        {
            // Typically picks up 'UTF-8' string
            Encoding enc = null;

            try {
                enc = Encoding.GetEncoding( mc[0].Groups[1].Value );
            }catch (Exception ) { }

            if( enc != null )
                return enc;
        }

        return Encoding.ASCII;      // Default fallback
    }

这足以从文件中读取前1024个字节,但我正在加载整个文件。

答案 7 :(得分:5)

如果有人正在寻找93.9%的解决方案。这对我有用:

public static class StreamExtension
{
    /// <summary>
    /// Convert the content to a string.
    /// </summary>
    /// <param name="stream">The stream.</param>
    /// <returns></returns>
    public static string ReadAsString(this Stream stream)
    {
        var startPosition = stream.Position;
        try
        {
            // 1. Check for a BOM
            // 2. or try with UTF-8. The most (86.3%) used encoding. Visit: http://w3techs.com/technologies/overview/character_encoding/all/
            var streamReader = new StreamReader(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), detectEncodingFromByteOrderMarks: true);
            return streamReader.ReadToEnd();
        }
        catch (DecoderFallbackException ex)
        {
            stream.Position = startPosition;

            // 3. The second most (6.7%) used encoding is ISO-8859-1. So use Windows-1252 (0.9%, also know as ANSI), which is a superset of ISO-8859-1.
            var streamReader = new StreamReader(stream, Encoding.GetEncoding(1252));
            return streamReader.ReadToEnd();
        }
    }
}

答案 8 :(得分:4)

我在Python中做过类似的事情。基本上,您需要来自各种编码的大量样本数据,这些数据由滑动的双字节窗口分解并存储在字典(散列)中,键入字节对,提供编码列表的值。

鉴于字典(哈希),您可以输入输入文本并:

  • 如果它以任何BOM字符开头(UTF-16-BE为'\ xfe \ xff',UTF-16-LE为'\ xff \ xfe',UTF-8为'\ xef \ xbb \ xbf'等) ),我按照建议处理
  • 如果没有,则获取足够大的文本样本,获取样本的所有字节对,并选择字典中建议最少的编码。

如果你还采样了的UTF编码文本,那么第二步将涵盖从第一步开始的那些。

到目前为止,它适用于我(样本数据和后续输入数据是各种语言的字幕),错误率降低。

答案 9 :(得分:3)

StreamReader类的构造函数采用'detect encoding'参数。

答案 10 :(得分:3)

工具“uchardet”使用每个字符集的字符频率分布模型做得很好。较大的文件和更多“典型”文件更有信心(显然)。

在ubuntu上,你只需apt-get install uchardet

在其他系统上,获取源,使用情况和这里的文档:https://github.com/BYVoid/uchardet

答案 11 :(得分:1)

如果您可以链接到C库,则可以使用libenca。见http://cihar.com/software/enca/。从手册页:

  

Enca读取给定的文本文件,或者没有给出标准输入,   并使用有关他们语言的知识(必须得到你的支持)和   解析,统计分析,猜测和黑魔法的混合物   确定他们的编码。

这是GPL v2。

答案 12 :(得分:0)

自从被问到这个问题后,10Y(!)已经过去了,我仍然没有提到MS的优秀,非GPL解决方案:IMultiLanguage2 API。

已经提到的大多数库都基于Mozilla的UDE - 浏览器已经解决了类似的问题似乎是合理的。我不知道什么是chrome的解决方案,但是因为IE 5.0 MS已经发布了它们,它是:

  1. 没有GPL和类似的许可问题,
  2. 可能永远支持和维护,
  3. 提供丰富的输出 - 编码/代码页的所有有效候选者以及置信度分数
  4. 令人惊讶的易于使用(这是一个单一的函数调用)。
  5. 这是一个原生的COM调用,但是由Carsten Zeumer编写的here's some very nice work,用于处理.net使用的互操作混乱。周围还有其他一些,但总的来说这个图书馆并没有得到应有的关注。

答案 13 :(得分:0)

感谢@ Erik Aronesty提及uchardet

同时,linux({1}}存在(相同的?)工具 或者,在cygwin上,您可能想要使用:chardet

请参阅:chardet man page: https://www.commandlinux.com/man-page/man1/chardetect.1.html

这将启发式地检测(猜测)每个给定文件的字符编码,并报告每个文件检测到的字符编码的名称和置信度。

答案 14 :(得分:0)

在AkelPad中打开文件(或只是复制/粘贴乱码文本),转到编辑 - &gt;选择 - &gt;重新编码... - &gt;检查&#34;自动检测&#34;。

答案 15 :(得分:0)

作为ITmeze帖子的插件,我已经使用此函数转换了Mozilla Universal Charset Detector的C#端口输出

    private Encoding GetEncodingFromString(string codePageName)
    {
        try
        {
            return Encoding.GetEncoding(codePageName);
        }
        catch
        {
            return Encoding.ASCII;
        }
    }

MSDN

答案 16 :(得分:0)

我实际上是在寻找一种通用的,而不是编程方式来检测文件编码,但我还没有找到。 我通过不同编码测试找到的是我的文本是UTF-7。

所以我第一次做的是: StreamReader文件= File.OpenText(fullfilename);

我不得不将其更改为: StreamReader file = new StreamReader(fullfilename,System.Text.Encoding.UTF7);

OpenText假设它是UTF-8。

您也可以像这样创建StreamReader 新的StreamReader(fullfilename,true),第二个参数意味着它应该尝试从文件的byteordermark中检测编码,但这在我的情况下不起作用。

答案 17 :(得分:0)

由于它基本上归结为启发式,因此可能有助于使用来自同一来源的先前接收文件的编码作为第一个提示。

大多数人(或应用程序)每次都以相同的顺序执行操作,通常在同一台机器上,所以很可能当Bob创建.csv文件并将其发送给Mary时,它总是会使用Windows -1252或他的机器默认的任何东西。

在可能的情况下,一些客户培训也不会受到伤害: - )

答案 18 :(得分:0)

遇到了同样的问题但是没有找到一个好的解决方案来自动检测它。 现在我正在使用PsPad(www.pspad.com);)工作正常

答案 19 :(得分:-1)

我在阅读文件时使用此代码检测Unicode和Windows默认的ansi代码页。对于其他编码,需要手动或通过编程来检查内容。这可以用于使用与打开时相同的编码来保存文本。 (我使用VB.NET)

'Works for Default and unicode (auto detect)
Dim mystreamreader As New StreamReader(LocalFileName, Encoding.Default) 
MyEditTextBox.Text = mystreamreader.ReadToEnd()
Debug.Print(mystreamreader.CurrentEncoding.CodePage) 'Autodetected encoding
mystreamreader.Close()