如何检测文本文件的编码

---恢复内容开始---

在遥远的2008年9月18日, 网友@ GvS 在stackoverflow上提了这么一个问题:

 

“如何检测文本文件的编码/内码页?”

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

“有没有办法(自动)检测文本文件的内码页呢?”

 

第一位回答的网友@ JV 是这样说的:

“你无法检测‘内码页’,你需要被告知使用的‘内码页’是什么。”

“你可以分析文本的字节并猜测它,但可能会带来一些奇怪的(滑稽的)的结果。”

“我没有办法自动检测,但我肯定notepad可以被诱骗到显示中文的英文文本。”

“无论如何,你需要阅读的内容: 

绝对是每个软件开发人员最低限度的,必须积极了解的Unicode和字符集.”

http://www.joelonsoftware.com/articles/Unicode.html

“具体一点,Joel说:关于编码的一个最重要的事实”

‘如果你完全忘记了我刚才解释的一切,请记住一个非常重要的事实。在不知道使用哪种编码的情况下生成一个字符串是没有意义的。你不能再将头伸进沙子里,假装“简单”文本是ASCII。 没有像纯文本那样的东西。’

“如果你有一个字符串,在内存中也好,在文件中也好,或者是电子邮件中的信息也好,你必须知道它的编码方式,否则无法正确的解释或者显示给用户”。

 

 

第二位网友@Tomer Gabel 没有直面问题而是写道:

“如果希望检测非UTF编码(即没有BOM),那么基本上就是文本的启发式(heuristics)和统计(statistical )分析。”

“你可能想看看关于通用字符集检测的Mozilla论文。”

http://www-archive.mozilla.org/projects/intl/UniversalCharsetDetection.html

 

针对这个回答,@Tao 纠正到:

“你说的 ‘如果你想检测非UTF编码(即没有BOM)’有点误导; unicode标准不建议向utf-8文档添加BOM! (并且这个建议或者缺乏这个建议是许多令人头疼的根源)。ref: en.wikipedia.org/wiki/Byte_order_mark#UTF-8 ”

 

@ ITmeze 直截了当的给出来c#的方案:

“你是否尝试过 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.");
        }
    }
} 

  

@ shoosh 认为内码页可以检测到

 

“ ’无法检测到内码页’ 

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

http://www.mozilla.org/projects/intl/detectorsrc.html

 

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

这是我刚刚使用Google发现的另一个”

http://chardet.feedparser.org/

 

但@ JV 进行了调侃: “启发式” - 所以浏览器没有完全检测到它,这是一个有根据的猜测。 “工作得很好” - 所以不是一直都工作? 听起来我们好像达成了一致

 

 

 

@TarmoPikaro 同学也找到了https://code.google.com/p/ude/ 这个解决方案,但觉得有点笨重:

“我需要一些基本的编码检测,基于4个第一字节和可能的xml字符集检测 - 所以我从互联网上拿了一些示例源代码并稍微地修改为以下(Java版本)。”

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

 

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

 

 

@Erik Aronesty 同学给出了在Ubuntu下的解决方案:

“工具‘uchardet’为每个字符集使用字符频率分布模型,所以工作的很好。”

“大文件和‘典型’文件,效果更好。”

“在Ubuntu上,通过命令: apt-get install uchardet”

“其它系统,可以在 https://github.com/BYVoid/uchardet 获取源码、用户和文档。”

 

这个答案比较有意思,因为在Related Projects中,囊括了Python\Ruby\Java\C#\Rust\C++\C等相关项目。哦这个项目本身也是c/c++的。

 

 

原文:https://stackoverflow.com/questions/90838/how-can-i-detect-the-encoding-codepage-of-a-text-file/5830273#5830273

posted @ 2018-03-08 12:01  envoy  阅读(4000)  评论(0编辑  收藏  举报