首页 文章

如何可靠地猜测MacRoman,CP1252,Latin1,UTF-8和ASCII之间的编码

提问于
浏览
96

在工作中似乎没有一周没有一些编码相关的conniption,灾难或灾难 . 这个问题通常来自程序员,他们认为他们可以在不指定编码的情况下可靠地处理“文本”文件 . 但你不能 .

因此,已经决定从此以后禁止文件的名称以 *.txt*.text 结尾 . 我们的想法是,这些扩展误导了偶然程序员对编码的沉闷,这会导致处理不当 . 没有任何扩展几乎会更好,因为至少你知道你不知道你有什么 .

但是,我们并不是那么远 . 相反,您需要使用以编码结尾的文件名 . 因此,对于文本文件,例如,这些将是 README.asciiREADME.latin1README.utf8 等 .

对于需要特定扩展名的文件,如果可以在文件本身内部指定编码,例如在Perl或Python中,那么您应该这样做 . 对于像Java源这样的文件,文件内部不存在这样的工具,您将把编码放在扩展名之前,例如 SomeClass-utf8.java .

对于输出,UTF-8应为 strongly 首选 .

但是对于输入,我们需要弄清楚如何处理名为 *.txt 的代码库中的数千个文件 . 我们想重命名所有这些以符合我们的新标准 . 但我们不可能全都注意它们 . 所以我们需要一个真正有用的库或程序 .

它们有各种ASCII,ISO-8859-1,UTF-8,Microsoft CP1252或Apple MacRoman . 虽然我们知道我们可以判断某些东西是否为ASCII,并且我们知道某些东西是否可能是UTF-8,但我们对8位编码感到困惑 . 因为我们在混合的Unix环境(Solaris,Linux,Darwin)中运行,大多数桌面都是Mac,所以我们有很多烦人的MacRoman文件 . 而这些尤其是一个问题 .

一段时间以来,我一直在寻找一种方法来以编程方式确定哪一个

  • ASCII

  • ISO-8859-1

  • CP1252

  • MacRoman

  • UTF-8

文件在,我还没有找到一个程序或库,可以可靠地区分这三种不同的8位编码 . 我们可能单独拥有超过一千个MacRoman文件,因此我们使用的任何字符集检测器都必须能够嗅出这些文件 . 我没看过任何东西可以解决这个问题 . 我对ICU charset detector library寄予厚望,但它无法处理MacRoman . 我也看过模块在Perl和Python中做同样的事情,但一次又一次它总是同样的故事:不支持检测MacRoman .

我正在寻找的是一个现有的库或程序,它可靠地确定文件所在的五种编码中的哪一种 - 并且最好是更多 . 特别是它必须区分我引用的三个3位编码, especially MacRoman . 这些文件超过99%的英文文本;其他语言中有一些,但并不多 .

如果是库代码,我们的语言首选项是Perl,C,Java或Python,并且按此顺序 . 如果它只是一个程序,那么我们并不关心它是什么语言,只要它是完整的源代码,在Unix上运行,并且完全没有阻碍 .

有没有其他人有这个随机编码的遗留文本文件的问题?如果是这样,你是如何尝试解决它的,你有多成功?这是我的问题中最重要的方面,但我也感兴趣的是你是否认为鼓励程序员用这些文件的实际编码来命名(或重命名)他们的文件将有助于我们避免将来出现这个问题 . 有没有人试图在制度基础上强制执行这个,如果是的话,是否成功,为什么?

是的,我完全理解为什么鉴于问题的性质,人们无法保证给出明确的答案 . 对于没有足够数据可用的小文件尤其如此 . 幸运的是,我们的文件很少 . 除随机 README 文件外,大多数文件的大小范围为50k到250k,而且许多文件的大小更大 . 任何超过几K的东西都保证是英文的 .

问题领域是生物医学文本挖掘,因此我们有时会处理广泛且极大的语料库,就像所有PubMedCentral的Open Access资源库一样 . 一个相当庞大的文件是BioThesaurus 6.0,为5.7千兆字节 . 这个文件特别烦人,因为它几乎都是UTF-8 . 然而,我相信,有些numbskull会在其中插入几行8位编码 - 微软CP1252 . 在你旅行之前需要一段时间 . :(

7 回答

  • 1

    “Perl,C,Java或Python,按顺序”:有趣的态度:-)

    “我们知道某些东西是否可能是UTF-8,这是一个很好的改变”:实际上,包含有意义文本的文件在使用高位设置字节的其他字符集中编码的机会将成功解码,因为UTF-8非常小 .

    UTF-8策略(至少首选语言):

    # 100% Unicode-standard-compliant UTF-8
    def utf8_strict(text):
        try:
            text.decode('utf8')
            return True
        except UnicodeDecodeError:
            return False
    
    # looking for almost all UTF-8 with some junk
    def utf8_replace(text):
        utext = text.decode('utf8', 'replace')
        dodgy_count = utext.count(u'\uFFFD') 
        return dodgy_count, utext
        # further action depends on how large dodgy_count / float(len(utext)) is
    
    # checking for UTF-8 structure but non-compliant
    # e.g. encoded surrogates, not minimal length, more than 4 bytes:
    # Can be done with a regex, if you need it
    

    一旦你确定它既不是ASCII也不是UTF-8:

    我所知道的Mozilla原点字符集检测器不支持MacRoman,在任何情况下都不能很好地处理8位字符集,特别是英语,因为AFAICT它们依赖于检查解码是否有意义语言,忽略标点字符,并基于该语言的各种文档 .

    正如其他人所说,你真的只有高位设置的标点字符可用来区分cp1252和macroman . 我建议在你自己的文件上训练一个Mozilla型模型,而不是莎士比亚或Hansard或KJV圣经,并考虑所有256个字节 . 我认为你的文件中没有标记(HTML,XML等) - 这会扭曲令人震惊的概率 .

    您提到的文件大多是UTF-8但无法解码 . 你也应该非常怀疑:

    (1)据称在ISO-8859-1中编码但包含"control characters"在0x80到0x9F范围内的文件...这是非常流行的HTML5标准草案说要解码所有声明为ISO-8859-1的HTML流CP1252 .

    (2)解码OK为UTF-8的文件,但生成的Unicode包含U 0080到U 009F范围内的“控制字符”...这可能是因为转码cp1252 / cp850(看到它发生!)/ etc文件来自UTF-8的“ISO-8859-1” .

    背景:我有一个湿的周日下午项目来创建一个基于Python的字符集检测器,它面向文件(而不是面向Web),并且适用于8位字符集,包括 legacy ** n ,如cp850和cp437 . 它__73895_ m对训练档案感兴趣;您的ISO-8859-1 / cp1252 / MacRoman文件与您期望任何人的代码解决方案一样"unencumbered"吗?

  • 7

    首先,简单的案例:

    ASCII

    如果您的数据不包含0x7F以上的字节,则它是ASCII . (或者是7位ISO646编码,但这些编码非常过时 . )

    UTF-8

    如果您的数据验证为UTF-8,那么您可以放心地假设它是UTF-8 . 由于UTF-8的严格验证规则,误报极为罕见 .

    ISO-8859-1对比windows-1252

    这两种编码之间的唯一区别是ISO-8859-1具有C1控制字符,其中windows-1252具有可打印的字符€,ƒ“...†‡‡Š<ŒŽ''”“• - 〜™š> œžŸ . 我见过很多使用弯引号或短划线的文件,但没有使用C1控制字符的文件 . 所以不要打扰他们,或ISO-8859-1,只需检测windows-1252 .

    那现在只留下一个问题 .

    你如何区分MacRoman和cp1252?

    这比较棘手 .

    未定义的字符

    在windows-1252中不使用字节0x81,0x8D,0x8F,0x90,0x9D . 如果它们出现,则假设数据是MacRoman .

    相同的字符

    两个编码中的字节0xA2(¢),0xA3(£),0xA9(©),0xB1(±),0xB5(μ)恰好相同 . 如果这些是唯一的非ASCII字节,那么无论选择MacRoman还是cp1252都无关紧要 .

    统计方法

    计算您知道为UTF-8的数据中的字符(非字节!)频率 . 确定最常用的字符 . 然后使用此数据确定cp1252或MacRoman字符是否更常见 .

    例如,在我刚刚对100篇随机英语维基百科文章进行的搜索中,最常见的非ASCII字符是 ·•–é°®’èö— . 基于这个事实,

    • 字节0x92,0x95,0x96,0x97,0xAE,0xB0,0xB7,0xE8,0xE9或0xF6表示windows-1252 .

    • 字节0x8E,0x8F,0x9A,0xA1,0xA5,0xA8,0xD0,0xD1,0xD5或0xE1表示MacRoman .

    计算cp1252建议字节和MacRoman建议字节,并选择最大的字节 .

  • 85

    Mozilla nsUniversalDetector(Perl绑定:Encode::Detect / Encode::Detect::Detector)经过百万倍的证明 .

  • 10

    我尝试这种启发式(假设你已经排除了ASCII和UTF-8):

    • 如果0x7f到0x9f,那么't appear at all, it'可能是ISO-8859-1,因为这些很少使用控制代码 .

    • 如果批次出现0x91到0x94,则可能是Windows-1252,因为那些是"smart quotes",到目前为止,该范围内最有可能用于英文文本的字符 . 更确切地说,你可以找对子 .

    • 否则,它是's MacRoman, especially if you see a lot of 0xd2 through 0xd5 (that',其中印刷报价在MacRoman中) .

    边注:

    对于像Java源这样的文件,文件内部不存在这样的工具,您将把编码放在扩展名之前,例如SomeClass-utf8.java

    Do not do this!!

    Java编译器期望文件名与类名匹配,因此重命名文件将使源代码无法编译 . 正确的做法是猜测编码,然后使用native2ascii工具将所有非ASCII字符转换为Unicode escape sequences .

  • 1

    正如您所发现的,没有完美的方法来解决这个问题,因为没有关于文件使用哪种编码的隐含知识,所有8位编码完全相同:字节集合 . 所有字节对所有8位编码都有效 .

    您可以期望的最好的是某种分析字节的算法,并且基于某种语言中使用的某个字节的概率以及某种编码将猜测文件使用的编码 . 但是,这必须知道文件使用哪种语言,并且当您具有混合编码的文件时变得完全无用 .

    从好的方面来说,如果您知道文件中的文本是用英文编写的,那么您不太可能注意到您决定使用该文件的编码有什么区别,因为所有提到的编码之间的差异都是本地化的编码的部分,用于指定通常不用于英语的字符 . 你可能会遇到一些麻烦,其中文本使用特殊格式或特殊版本的标点符号(例如CP1252有几个版本的引号字符),但对于文本的要点,可能没有问题 .

  • 3

    如果你可以检测宏的每个编码EXCEPT,那么假设无法解密的那些编码在宏观中是合乎逻辑的 . 换句话说,只需创建一个无法处理的文件列表,并像处理它们一样处理它们 .

    对这些文件进行排序的另一种方法是制作一个基于服务器的程序,允许用户决定哪个编码没有乱码 . 当然,它将在公司内部,但每天有100名员工做几件事,你很快就会完成数千个文件 .

    最后,将所有现有文件转换为单一格式不是更好,并要求新文件采用该格式 .

  • 5

    有没有其他人有这个随机编码的遗留文本文件的问题?如果是这样,你是如何尝试解决它的,你有多成功?

    我目前正在编写一个将文件转换为XML的程序 . 它必须自动检测每个文件的类型,这是确定文本文件编码问题的超集 . 为了确定编码我使用的是贝叶斯方法 . 也就是说,我的分类代码计算文本文件对其理解的所有编码具有特定编码的概率(似然) . 然后程序选择最可能的解码器 . 对于每种编码,贝叶斯方法都是这样的 .

    • 根据每个编码的频率设置文件在编码中的初始(先验)概率 .

    • 在文件中依次检查每个字节 . 查找字节值以确定存在的字节值与实际处于该编码中的文件之间的相关性 . 使用该相关性来计算文件在编码中的新(后验)概率 . 如果要检查更多字节,请在检查下一个字节时使用该字节的后验概率作为先验概率 .

    • 当你到达文件的末尾(我实际上只查看前1024个字节)时,你所具有的可能性是文件在编码中的概率 .

    可以看出,贝叶斯定理变得非常容易,如果不是计算概率,你计算信息内容,这是几率的对数: info = log(p / (1.0 - p)) .

    您必须通过检查手动分类的文件语料库来计算initail先验概率和相关性 .

相关问题