代码之家  ›  专栏  ›  技术社区  ›  GvS

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

  •  283
  • GvS  · 技术社区  · 16 年前

    .txt , .csv ,等等)来自不同的来源。在读取时,这些文件有时包含垃圾,因为这些文件是在不同/未知的代码页中创建的。

    这个 detectEncodingFromByteOrderMarks ,在 StreamReader 建造师,为 UTF8 和其他unicode标记的文件,但我正在寻找一种检测代码页的方法,如 ibm850 windows1252 .


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

    • 我已经创建了一个小应用程序,用户可以使用它打开文件,并输入一个文本,当使用正确的代码页时,用户知道它将出现在文件中。
    • 循环浏览所有代码页,并用用户提供的文本显示给出解决方案的代码页。
    • 如果弹出多个as-one代码页,请用户指定更多文本。
    20 回复  |  直到 10 年前
        1
  •  266
  •   Arjun Sol    11 年前

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

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

    特别是乔尔说:

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

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

    如果内存、文件或电子邮件中有字符串,则必须知道其编码方式,否则无法正确解释或显示给用户。

        3
  •  29
  •   ITmeze    12 年前

    你试过了吗 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.");
            }
        }
    }    
    
        4
  •  16
  •   elixenide Ren    10 年前

    您无法检测代码页

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

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

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

        5
  •  10
  •   Tao    13 年前

    我知道现在回答这个问题已经很晚了,而且这个解决方案对一些人没有吸引力(因为它以英语为中心,缺乏统计/实证测试),但它对我来说非常有效,特别是在处理上传的CSV数据方面:

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

    • 内置BOM检测
    • 可自定义的默认/回退编码

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

        6
  •  7
  •   alex    14 年前
        7
  •  7
  •   TarmoPikaro    11 年前

    寻找不同的解决方案,我发现

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

    这个解决方案有点重。

    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字节就足够了,但我正在加载整个文件。

        8
  •  7
  •   Markus    8 年前

    如果有人正在寻找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();
            }
        }
    }
    
        9
  •  4
  •   tzot    16 年前

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

    如果您还对UTF编码的文本进行了采样 从任何BOM表开始,第二步将涵盖从第一步滑出的BOM表。

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

        10
  •  4
  •   Erik Aronesty    11 年前

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

    apt-get install uchardet .

    https://github.com/BYVoid/uchardet

        11
  •  3
  •   leppie    16 年前

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

        12
  •  1
  •   Nick Matteo    11 年前

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

    Enca读取给定的文本文件,或在没有给定文本文件时读取标准输入, 确定它们的编码。

    这是GPL v2。

        13
  •  0
  •   DeeCee    16 年前

    遇到了同样的问题,但还没有找到自动检测问题的好方法。 现在我用PsPad(www.PsPad.com)来做这个;)很好

        14
  •  0
  •   devstuff    15 年前

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

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

    在可能的情况下,一点客户培训也不会有什么坏处:-)

        15
  •  0
  •   Flexo - Save the data dump sunny moon    11 年前

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

    我第一次做的是: StreamReader文件=file.OpenText(完整文件名);

    我不得不把它改成: StreamReader文件=新的StreamReader(完整文件名,System.Text.Encoding.UTF7);

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

        16
  •  0
  •   plavozont    8 年前

    在AkelPad中打开文件(或只是复制/粘贴一个乱码文本),进入编辑->选择->重新编码-&燃气轮机;选中“自动检测”。

        17
  •  0
  •   ViRuSTriNiTy    6 年前

    作为ITmeze post的附加组件,我使用此函数为Mozilla Universal Charset Detector转换C#port的输出

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

    MSDN

        18
  •  0
  •   Schlacki    6 年前

    谢谢@ Erik Aronesty 提及 uchardet .

    同时,linux也有(相同的?)工具: chardet
    或者,在cygwin上,您可能需要使用: chardetect .

    见: chardet man page: https://www.commandlinux.com/man-page/man1/chardetect.1.html

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

        19
  •  -1
  •   Hannah Vernon    9 年前

    '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()
    
        20
  •  -1
  •   Ofek Shilon    6 年前

    10年(!)已经过去,因为这是问,但我仍然没有看到提到MS的良好,非GPL'ed解决方案: IMultiLanguage2 应用程序编程接口。

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

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

    here's some very nice work 由Carsten Zeumer编写,用于处理.net使用的互操作混乱。周围还有其他一些图书馆,但总的来说,这个图书馆没有得到应有的重视。