305

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

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

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


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

我们收到的文件来自最终用户,他们对代码页一无所知。接收者也是最终用户,到目前为止,这就是他们对代码页的了解:代码页存在,而且很烦人。

解决方案:

  • 在记事本中打开接收到的文件,查看一段乱码。如果有人叫弗朗索瓦什么的,用你的人类智慧你可以猜到这一点。
  • 我创建了一个小应用程序,用户可以使用它打开文件,并输入用户知道在使用正确代码页时它将出现在文件中的文本。
  • 循环遍历所有代码页,并使用用户提供的文本显示给出解决方案的代码页。
  • 如果弹出更多 as 一个代码页,请要求用户指定更多文本。
4

21 回答 21

267

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

无论如何,这是您需要阅读的内容: 每个软件开发人员绝对、肯定必须了解 Unicode 和字符集的绝对最低要求(没有借口!)

具体来说,乔尔说:

关于编码的一个最重要的事实

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

如果您在内存、文件或电子邮件中有一个字符串,您必须知道它的编码是什么,否则您将无法正确解释它或将其显示给用户。

于 2008-09-18T08:30:29.777 回答
34

如果您希望检测非 UTF 编码(即没有 BOM),那么您基本上需要对文本进行启发式和统计分析。您可能想查看关于通用字符集检测的 Mozilla 论文相同的链接,通过 Wayback Machine 提供更好的格式)。

于 2008-09-18T08:23:13.617 回答
30

您是否尝试过用于 Mozilla Universal Charset Detector 的 C# 端口

来自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.");
        }
    }
}    
于 2012-07-23T10:47:38.277 回答
16

您无法检测到代码页

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

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

这是我刚刚使用 Google 找到的另一个:

于 2008-09-18T09:04:51.567 回答
10

我知道这个问题已经很晚了,而且这个解决方案不会吸引一些人(因为它以英语为中心的偏见和缺乏统计/实证测试),但它对我来说效果很好,特别是对于处理上传的 CSV 数据:

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

好处:

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

注意:我是写这门课的人,所以显然对它持保留态度!:)

于 2011-04-29T09:20:17.950 回答
7

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

于 2010-08-06T19:13:52.677 回答
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 个字节就足够了,但我正在加载整个文件。

于 2013-10-19T09:57:22.593 回答
6

如果有人正在寻找 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();
        }
    }
}
于 2016-02-09T14:31:38.047 回答
4

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

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

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

如果您还对以任何 BOM 开头的 UTF 编码文本进行了采样,则第二步将覆盖从第一步中漏掉的文本。

到目前为止,它对我有用(示例数据和后续输入数据是各种语言的字幕),并且错误率降低了。

于 2008-09-18T09:03:38.420 回答
4

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

在 ubuntu 上,你只是apt-get install uchardet.

在其他系统上,请在此处获取源代码、用法和文档:https ://github.com/BYVoid/uchardet

于 2013-12-03T14:20:41.193 回答
3

StreamReader 类的构造函数采用“检测编码”参数。

于 2008-09-18T08:04:28.747 回答
1

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

于 2008-09-18T08:25:40.180 回答
1

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

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

它是 GPL v2。

于 2013-03-27T03:25:12.507 回答
1

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

于 2016-08-22T03:02:43.260 回答
0

由于它基本上归结为启发式方法,因此将以前从同一来源接收到的文件的编码用作第一个提示可能会有所帮助。

大多数人(或应用程序)每次都以几乎相同的顺序做事,通常是在同一台机器上,所以当 Bob 创建一个 .csv 文件并将其发送给 Mary 时,它很可能总是使用 Windows-1252 或无论他的机器默认是什么。

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

于 2008-12-29T19:42:00.367 回答
0

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

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

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

OpenText 假定它是 UTF-8。

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

于 2010-08-20T12:31:49.920 回答
0

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

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

MSDN

于 2015-09-03T09:37:33.990 回答
0

感谢@Erik Aronesty提及uchardet

同时(相同?)工具存在于 linux: chardet.
或者,在 cygwin 上,您可能想要使用:chardetect.

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

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

于 2018-05-07T12:26:08.843 回答
0

尝试通过键入 cpanm Text::Unaccent::PurePerl 安装 perl 模块 Text::Unaccent 这会生成一个 build.log 文件,该文件在某些​​应用程序中显示为中文,在其他应用程序中显示为英文 cpanm 是初始文本,如果你幸运的话,这是一个合理的尝试语言中足够有空格的方法是通过统计检验比较单词的分布频率

于 2022-02-12T15:06:08.743 回答
-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()
于 2012-06-02T10:39:54.770 回答
-1

自从被问到这个问题以来已经过去了 10 年(!),但我仍然没有看到提到 MS 的良好的非 GPL 解决方案:IMultiLanguage2 API。

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

  1. 没有 GPL 和类似的许可问题,
  2. 可能永远得到支持和维护,
  3. 提供丰富的输出 - 编码/代码页的所有有效候选者以及置信度分数,
  4. 非常容易使用(它是一个单一的函数调用)。

这是一个本地 COM 调用,但这是 Carsten Zeumer的一些非常出色的工作,它处理了 .net 使用的互操作混乱。还有一些其他的,但总的来说,这个库并没有得到应有的关注。

于 2018-06-03T20:45:14.520 回答