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

tch*_*ist 97 python java macos perl character-encoding

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

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

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

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

对于输出,UTF-8是非常优选的.

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

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

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

  1. ASCII
  2. ISO-8859-1
  3. CP1252
  4. 的MacRoman
  5. UTF-8

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

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

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

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

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

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

dan*_*n04 85

首先,简单的案例:

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建议字节,并选择最大的字节.

  • 终于开始实施这个了.结果发现维基百科不是很好的培训数据.从1k随机en.wikipedia文章,不包括LANGUAGES部分,我获得了50k unASCII代码点,但分布不可信:中间点和子弹太高,&c&c&c.所以我使用了全UTF8 PubMed Open Access语料库,挖掘了+ 14M unASCII代码点.我使用这些来构建所有8位编码的相对频率模型,比你的更好,但基于这个想法.这证明了对生物医学文本(目标领域)编码的高度预测性.我应该发表这个.谢谢! (10认同)
  • 我已经接受了你的答案,因为没有更好的答案能够出现,而且你写好了我一直在修补的问题.我确实有程序可以嗅出这些字节,尽管你的数字是我自己提出的数字的两倍. (6认同)
  • 我还没有任何MacRoman文件,但不会使用CR作为行分隔符提供有用的测试.这适用于旧版本的Mac OS,虽然我不知道OS9. (5认同)

Mic*_*rdt 7

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

  • 如果0x7f到0x9f根本没有出现,则可能是ISO-8859-1,因为那些很少使用控制代码.
  • 如果批次出现0x91到0x94,则可能是Windows-1252,因为这些是"智能引号",到目前为止,该范围内最有可能用于英文文本的字符.更确切地说,你可以找对子.
  • 否则,它是MacRoman,特别是如果你看到很多0xd2到0xd5(这是排版引号在MacRoman中).

边注:

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

不要这样做!!

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

  • Stoopid kompilor!不,我们不能告诉人们他们只能使用ASCII; 这不再是20世纪60年代了.如果有一个@encoding注释,那么源代码在特定编码中的事实不会被强制存储在源代码外部,这不是一个问题,这是Java的一个非常愚蠢的缺点,Perl和Python都没有这个缺点. .它应该在源头.这不是我们的主要问题; 这是1000个`*.text`文件. (7认同)
  • @ dan04:您可以对XML,HTML或其他任何地方的编码声明说同样的话.但正如这些示例一样,如果它是在标准API中定义的,那么大多数使用源代码的工具(尤其是编辑器和IDE)都会支持它,这将非常可靠地防止人们*意外*创建内容编码所做的文件不符合声明. (4认同)
  • "Java编译器希望文件名与类名匹配." 仅当文件定义顶级公共类时,此规则才适用. (4认同)
  • @tchrist:编写自己的注释处理器来支持这样的注释实际上并不是那么难.仍然是一个令人尴尬的疏忽,没有在标准API中拥有它. (3认同)

Joh*_*hin 6

"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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

(1)据称在ISO-8859-1中编码但包含0x80到0x9F范围内的"控制字符"的文件......这种情况非常普遍,HTML5标准草案要求解码声明为ISO-8859的所有 HTML流-1使用cp1252.

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

背景:我有一个湿的周日下午项目来创建一个基于Python的字符集检测器,它是面向文件的(而不是面向Web的),并且适用于8位字符集,包括legacy ** ncp850和cp437.它还没有接近黄金时间.我对培训文件感兴趣; 您的ISO-8859-1/cp1252/MacRoman文件是否与您期望任何人的代码解决方案一样"无阻碍"?