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

2020/12/05 21:52 · python ·  · 0评论

在工作中,似乎没有一周没有编码相关的混乱,灾难或灾难。问题通常来自程序员,他们认为他们无需指定编码就可以可靠地处理“文本”文件。但是你不能。

因此,已决定从此以后禁止文件以*.txt结尾的文件名*.text他们的想法是,这些扩展使临时程序员误以为对编码不满意,这会导致处理不当。根本没有扩展将是更好的选择,因为至少您知道自己不知道拥有什么。

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

对于需要特定扩展名的文件,如果可以在文件本身内部指定编码,例如在Perl或Python中,则应这样做。对于像Java源这样的文件,其中文件内部没有这样的功能,您可以将编码放在扩展名之前,例如SomeClass-utf8.java

对于输出,强烈建议使用UTF-8

但是作为输入,我们需要弄清楚如何处理代码库中名为的数千个文件*.txt我们想重命名所有这些以适应我们的新标准。但是我们不可能全都盯着他们。因此,我们需要一个实际起作用的库或程序。

这些格式有ASCII,ISO-8859-1,UTF-8,Microsoft CP1252或Apple MacRoman。尽管我们知道我们可以判断某些东西是否是ASCII,并且知道是否可能是UTF-8还是一个很好的改变,但我们对8位编码感到困惑。因为我们在大多数台式机为Mac的混合Unix环境(Solaris,Linux,Darwin)中运行,所以我们有很多烦人的MacRoman文件。这些尤其是一个问题。

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

  1. ASCII码
  2. ISO-8859-1
  3. CP1252
  4. 麦克罗曼
  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 GB。该文件特别令人讨厌,因为它几乎都是UTF-8。但是,我相信有些numbskull会以一些8位编码插入其中的几行,即Microsoft CP1252。您需要花很长时间才能踏上那条路。:(

首先,简单的情况:

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条随机英语Wikipedia文章执行的搜索中,最常见的非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建议字节,并选择最大的一个。

Mozilla nsUniversalDetector(Perl绑定:Encode :: Detect / Encode :: Detect :: Detector)已被证明了百万倍。

我尝试进行这种试探(假设您已经排除了ASCII和UTF-8):

  • 如果根本没有出现0x7f到0x9f,则可能是ISO-8859-1,因为它们是很少使用的控制代码。
  • 如果从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
# 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类型的模型,而不是莎士比亚,《议事录》或《圣经》,并考虑所有256个字节。我认为您的文件中没有标记(HTML,XML等),这会使某些令人震惊的概率失真。

您提到的文件大多为UTF-8,但无法解码。您还应该非常怀疑:

(1)据称是用ISO-8859-1编码的文件,但包含范围在0x80至0x9F(包括0x80至0x9F)内的“控制字符” ...这太普遍了,以至于HTML5标准草案表示要解码所有声明为ISO-8859的HTML流-1使用cp1252。

(2)可以将OK解码为UTF-8的文件,但所得的Unicode包含范围在U + 0080至U + 009F(含)范围内的“控制字符” ...这可能是由于对cp1252 / cp850进行代码转换(见它发生了!)/等等文件从“ ISO-8859-1”到UTF-8。

背景:我有一个星期天下午下午的项目,以创建一个基于Python的字符集检测器,该检测器面向文件(而不是面向Web),并且与8位字符集(包括legacy ** ncp850和cp437)兼容。还没到黄金时间。我对培训文件感兴趣;您的ISO-8859-1 / cp1252 / MacRoman文件是否像您期望任何人的代码解决方案一样“不受阻碍”?

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

您所希望的最好的办法是某种算法,可以分析字节,并基于以某种语言以某种编码使用某种字节的概率,可以猜测出文件使用的编码方式。但这必须知道文件使用哪种语言,并且当您使用混合编码的文件时,它变得完全无用。

从好的方面来说,如果您知道文件中的文本是用英语编写的,那么您决定使用该文件的任何编码都不会引起任何差异,因为所有上述编码之间的差异都已本地化。编码部分,用于指定通常在英语中不常用的字符。在文本使用特殊格式或特殊版本的标点符号(例如CP1252具有引号字符的多个版本)的情况下,您可能会遇到一些麻烦,但是对于文本的要旨而言,可能没有任何问题。

如果您可以检测到除宏人以外的所有编码,那么可以合理地假设无法解密的是宏人。换句话说,只需列出无法处理的文件,然后将其视为宏文件即可处理。

排序这些文件的另一种方法是制作一个基于服务器的程序,该程序允许用户确定哪种编码不乱码。当然,这将在公司内部,但是如果有100名员工每天做几次工作,那么您很快就会拥有成千上万的文件。

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

有没有其他人遇到过这样的问题:随机编码成千上万的旧文本文件?如果是这样,您是如何尝试解决它的?您的成功程度如何?

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

  1. 根据每次编码的频率,设置文件在编码中的初始(优先)概率。
  2. 依次检查文件中的每个字节。查找字节值,以确定该字节值存在与该编码中实际存在的文件之间的相关性。使用该相关性来计算文件在编码中的新(后验)概率。如果要检查的字节更多,请在检查下一个字节时将该字节的后验概率用作先验概率。
  3. 当您到达文件末尾时(我实际上仅查看前1024个字节),则具有的可能性就是文件处于编码状态的可能性。

它蒸发贝叶斯定理变得非常容易,如果不是计算概率,你计算做信息内容,这是对数赔率info = log(p / (1.0 - p))

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

由公认的答案所带头https://stackoverflow.com/a/4200765/2731103我可以改进红宝石宝石“夏洛特”,以识别所请求的编码大部分正确。

我们在生产环境中将其用于导入之前检测CSV文件编码

那是合理的部分(Ruby)

UTF8HASBOM = /^\xEF\xBB\xBF/n      #  [239, 187, 191]
UTF32LEBOM = /^\xFF\xFE\x00\x00/n  # [255, 254, 0, 0]
UTF32BEBOM = /^\x00\x00\xFE\xFF/n  # [0, 0, 254, 255]

UTF16LEBOM = /^\xFF\xFE/n                # [255, 254]
UTF16BEBOM = /^\xFE\xFF/n                # [254, 255]

NOTIN1BYTE = /[\x00-\x06\x0B\x0E-\x1A\x1C-\x1F\x7F]/n
NOTISO8859 = /[\x00-\x06\x0B\x0E-\x1A\x1C-\x1F\x7F\x80-\x84\x86-\x9F]/n

# Information to identify MacRoman
# https://stackoverflow.com/questions/4198804/
NOTINCP1252 = /[\x81\x8D\x8F\x90\x9D]/n
CP1252CHARS = /[\x92\x95\x96\x97\xAE\xB0\xB7\xE8\xE9\xF6]/n
MCROMNCHARS = /[\x8E\x8F\x9A\xA1\xA5\xA8\xD0\xD1\xD5\xE1]/n
detect.force_encoding('BINARY') # Needed to prevent non-matching regex charset.
sample = detect[0..19]     # Keep sample string under 23 bytes.
detect.sub!(UTF8HASBOM, '') if sample[UTF8HASBOM] # Strip any UTF-8 BOM.

# See: http://www.daniellesucher.com/2013/07/23/ruby-case-versus-if/
if    sample.ascii_only? && detect.force_encoding('UTF-8').valid_encoding?

elsif sample[UTF32LEBOM] && detect.force_encoding('UTF-32LE').valid_encoding?
elsif sample[UTF32BEBOM] && detect.force_encoding('UTF-32BE').valid_encoding?
elsif sample[UTF16LEBOM] && detect.force_encoding('UTF-16LE').valid_encoding?
elsif sample[UTF16BEBOM] && detect.force_encoding('UTF-16BE').valid_encoding?

elsif detect.force_encoding('UTF-8').valid_encoding?

elsif detect.force_encoding('BINARY')[NOTISO8859].nil?
  detect.force_encoding('ISO-8859-1')

elsif detect.force_encoding('BINARY')[NOTIN1BYTE].nil?

  if  detect.force_encoding('BINARY')[NOTINCP1252].nil? &&
            detect.force_encoding('BINARY').scan(MCROMNCHARS).length < detect.force_encoding('BINARY').scan(CP1252CHARS).length

      detect.force_encoding('Windows-1252')
  else
      detect.force_encoding('MacRoman')
  end

else  detect.force_encoding('BINARY')
end
本文地址:http://python.askforanswer.com/ruhekekaodicaicemacromancp1252latin1utf-8heasciizhijiandebianma.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!