如何可靠猜测MacRoman,CP1252,Latin1,UTF-8和ASCII之间的编码
在工作中,似乎没有一周没有一些编码相关的内涵,灾难或灾难。 这个问题通常来自程序员,他们认为他们可以在不指定编码的情况下可靠地处理“文本”文件。 但是你不能。
所以决定今后禁止以*.txt
或*.text
结尾的文件。 这样的想法是,这些扩展误导了偶然的程序员对编码的沉闷自满,这导致不正确的处理。 根本就没有延伸,因为至less你知道你不知道你有什么。
但是,我们并不想走得太远。 相反,您将需要使用以编码结尾的文件名。 所以对于文本文件,例如,这些就像README.ascii
, README.latin1
, README.utf8
等
对于需要特定扩展名的文件,如果可以在文件本身内指定编码,例如Perl或Python,那么您应该这样做。 对于文件中没有这样的工具的文件,例如Java源文件,您将在扩展之前放置编码,例如SomeClass-utf8.java
。
对于输出,UTF-8将是强烈的首选。
但是对于input,我们需要弄清楚如何处理名为*.txt
的代码库中的数千个文件。 我们想重新命名所有这些以符合我们的新标准。 但是,我们不可能把所有的东西都放在眼里。 所以我们需要一个实际工作的图书馆或者程序。
这些是在ASCII,ISO-8859-1,UTF-8,微软CP1252,或苹果MacRoman各种。 虽然我们知道我们可以判断出是ASCII是什么,而且我们知道是否有可能是UTF-8的一个很好的变化,但我们对8位编码难以理解。 因为我们运行在混合的Unix环境(Solaris,Linux,Darwin)中,而大多数桌面机都是Mac,所以我们有很多恼人的MacRoman文件。 而这些尤其是一个问题。
一段时间以来,我一直在寻找一种方法来编程确定哪一个
- ASCII
- ISO-8859-1
- CP1252
- 的MacRoman
- UTF-8
一个文件是在,我还没有find一个程序或库,可以可靠地区分这三个不同的8位编码。 我们可能有超过一千个MacRoman文件,所以无论使用什么字符集检测器,都必须能够嗅出这些文件。 我看过的没有什么可以pipe理的。 我对ICU字符集检测器库有很大的希望,但是却无法处理MacRoman。 我也研究过在Perl和Python中做同样的事情的模块,但是总是一样的:不支持检测MacRoman。
因此,我所寻找的是一个现有的图书馆或程序,可以可靠地确定这五个编码中哪一个是文件所在的位置,最好是多于这个编码。 特别是它必须区分我引用的三种3位编码, 尤其是MacRoman 。 这些文件超过99%的英文文本; 在其他语言中有一些,但不是很多。
如果是库代码,我们的语言偏好是按照Perl,C,Java或Python的顺序。 如果它只是一个程序,那么我们并不关心它是什么语言,只要它来自全部源代码,在Unix上运行,并且完全没有妨碍。
有没有其他人有这个问题的随机编码的十亿遗留文本文件? 如果是这样,你是怎么试图解决这个问题的,你是多么的成功? 这是我的问题中最重要的一个方面,但是我也很感兴趣的是,是否鼓励程序员使用这些文件的实际编码来命名(或重命名)它们的文件,这将有助于我们避免将来出现问题。 有没有人曾试图在体制上强制执行这项规定,如果是的话,是否成功?为什么?
是的,我完全明白为什么在这个问题的性质上不能保证一个明确的答案。 对于那些没有足够数据的小文件尤其如此。 幸运的是,我们的文件很less。 除了随机的README
文件外,大多数的大小范围是50k到250k,而且很多都是大的。 任何超过几K的大小都保证是英文的。
问题领域是生物医学文本挖掘,所以我们有时处理广泛的和非常大的语料库,像所有PubMedCentral的开放式访问存储库。 BioThesaurus 6.0是一个相当巨大的文件,5.7G。 这个文件特别烦人,因为它几乎都是UTF-8。 然而,我相信一些麻烦的事情发生了,并且在一些8位编码 – 微软CP1252中插入了几行。 在那之前需要一段时间的旅行。 🙁
首先,简单的情况是:
ASCII
如果你的数据不包含高于0x7F的字节,那么它是ASCII。 (或者是一个7位的ISO646编码,但是那些已经过时了。)
UTF-8
如果您的数据validation为UTF-8,那么您可以放心地假定它是 UTF-8。 由于UTF-8严格的validation规则,误报极其罕见。
ISO-8859-1与Windows-1252
这两种编码之间的唯一区别是ISO-8859-1具有C1控制字符,其中windows-1252具有可打印的字符。“?”????????????????? œžŸ。 我见过很多使用curl引号或破折号的文件,但没有一个使用C1控制字符。 所以不要打扰他们,或ISO-8859-1,只是检测Windows-1252。
现在只剩下一个问题。
你如何区分MacRoman和cp1252?
这是非常棘手的。
未定义的字符
字节0x81,0x8D,0x8F,0x90,0x9D不在windows-1252中使用。 如果发生,则假定数据是MacRoman。
相同的字符
两个编码中的字节0xA2(¢),0xA3(£),0xA9(©),0xB1(±),0xB5(μ)碰巧是相同的。 如果这些是唯一的非ASCII字节,那么selectMacRoman还是cp1252并不重要。
统计方法
在你知道的UTF-8数据中计算字符(不是字节)的频率。 确定最频繁的字符。 然后使用此数据来确定cp1252或MacRoman字符是否更常见。
例如,在一次search中,我只对100个随机英语维基百科文章进行了search,最常见的非ASCII字符是·•–é°®'èö—
基于这个事实,
- 字节0x92,0x95,0x96,0x97,0xAE,0xB0,0xB7,0xE8,0xE9或0xF6build议windows-1252。
- 字节0x8E,0x8F,0x9A,0xA1,0xA5,0xA8,0xD0,0xD1,0xD5或0xE1build议MacRoman。
将cp1252build议字节和MacRomanbuild议字节计算在一起,并以最大者为准。
Mozilla nsUniversalDetector (Perl绑定: Encode :: Detect / Encode :: Detect :: Detector )经过了百万年的validation。
我试图在这样的启发式(假设你已经排除了ASCII和UTF-8):
- 如果0x7f到0x9f完全不出现,那很可能是ISO-8859-1,因为那些是很less使用的控制代码。
- 如果0x91到0x94出现很多,可能是Windows-1252,因为那些是“聪明的引号”,是迄今为止在英文文本中使用的那个范围内最可能的字符。 更确定的是,你可以找对。
- 否则,它是MacRoman,特别是如果你看到很多0xd2到0xd5(这是MacRoman中的印刷引号)。
边注:
对于文件中没有这样的工具的文件,例如Java源文件,您将在扩展之前放置编码,比如SomeClass-utf8.java
不要这样做!!
Java编译器期望文件名与类名匹配,所以重命名这些文件将使源代码不可编译。 正确的事情是猜测编码,然后使用native2ascii
工具将所有非ASCII字符转换为Unicode转义序列 。
“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 # eg 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是依靠检查解码是否在给定的语言,忽略标点符号,并以该语言中广泛的文件select为基础。
正如其他人所说,你只有高位设置的标点符号才能区分cp1252和macroman。 我build议你在自己的文档上培训一个Mozillatypes的模型,而不是莎士比亚或者Hansard或者KJV圣经,并且考虑所有的256字节。 我认为你的文件中没有标记(HTML,XML等),这会歪曲概率是令人震惊的。
您提到的文件大多数是UTF-8,但无法解码。 你也应该非常怀疑:
(1)据称在ISO-8859-1中编码的文件,但包含范围在0x80到0x9F之间的“控制字符”…这是非常stream行的,以至于HTML5标准草案对解码所有声明为ISO-8859的HTMLstream-1使用cp1252。
(2)将OK解码为UTF-8的文件,但是由此产生的Unicode包含范围为U + 0080到U + 009F的“控制字符”…这可能是由于转码cp1252 / cp850(见过!)/ etc文件从“ISO-8859-1”转换为UTF-8。
背景:我有一个潮湿的星期天下午的项目来创build一个基于Python的字符集检测器,它是面向文件的(而不是面向Web的),并且适用于8位字符集,包括像cp850和cp437这样的legacy ** n
字符集。 目前还没有黄金时段。 我对训练档案感兴趣 你的ISO-8859-1 / cp1252 / MacRoman文件是否像你所期望的任何代码解决scheme一样“无障碍”?
正如你所发现的,没有完美的方法来解决这个问题,因为如果没有关于文件使用哪种编码的隐含知识,所有的8位编码都是完全相同的:字节集合。 所有字节对所有8位编码均有效。
最好的希望是某种分析字节的algorithm,并且基于特定语言中某个特定字节的概率使用某种编码将猜测文件使用什么编码。 但是,必须知道该文件使用哪种语言,并且在具有混合编码的文件时变得完全无用。
另一方面,如果您知道文件中的文本是用英文编写的,那么无论您决定使用哪个编码文件,您都不会注意到任何差异,因为所有提到的编码之间的差异都是本地化的这些编码的部分指定了英文中通常不使用的字符。 在文本使用特殊格式或特殊版本的标点符号(例如CP1252有几个版本的引号字符)的情况下,可能会遇到一些麻烦,但对于文本的要点,可能没有问题。
如果你能检测出除macros观人之外的每一种编码,那么假设那些不能被破译的macros观人就是合乎逻辑的。 换句话说,只要制作一个无法处理的文件列表,就好像他们是macros观人物一样。
sorting这些文件的另一种方法是制作一个基于服务器的程序,允许用户决定哪些编码不是乱码。 当然,这将在公司内部,但是每天有100名员工在做几件事情,您将立即完成数千个文件。
最后,将所有现有文件转换为单一格式并不是更好,并且要求新文件采用这种格式。
有没有其他人有这个问题的一个十亿遗留文本文件随机编码? 如果是这样,你是怎么试图解决这个问题的,你是多么的成功?
我目前正在编写一个将文件转换为XML的程序。 它必须自动检测每个文件的types,这是确定文本文件编码问题的一个超集。 为了确定编码,我使用了贝叶斯方法。 也就是说,我的分类代码计算一个文本文件对所有它理解的编码有一个特定编码的概率(可能性)。 程序然后select最可能的解码器。 贝叶斯方法对每个编码都是这样工作的。
- 根据每种编码的频率设置文件在编码中的初始( 先验 )概率。
- 在文件中依次检查每个字节。 查找字节值以确定该字节值与实际在该编码中的文件之间的相关性。 使用该相关性计算文件在编码中的新( 后验 )概率。 如果你有更多的字节要检查,当你检查下一个字节时,使用该字节的后验概率作为先验概率。
- 当你到达文件的末尾(实际上我只看最前面的1024个字节)时,你所拥有的可靠性就是文件处于编码状态的可能性。
它certificate了贝叶斯定理变得非常容易,如果不是计算概率,而是计算信息内容 ,这是对数的几率 : info = log(p / (1.0 - p))
。
您将必须通过检查您手动分类的文件的语料库来计算初始先验概率和相关性。