【问题标题】:Detect actual charset encoding in UTF检测 UTF 中的实际字符集编码
【发布时间】:2015-01-08 03:48:03
【问题描述】:

需要使用某种映射或启发式方法检测字符串编码的好工具。

例如字符串:áÞåàÐÝØÒ ÜÝÞÓÞ ßàØÛÞÖÕÝØÙ Java, ÜÞÖÝÞ ×ÐÝïâì Òáî ÔÞáâãßÝãî ßÐÜïâì

预期:сохранив много приложений Java, можно занять всю доступную память

编码为“ISO8859-5”。当我尝试使用以下库检测它时,结果是“UTF-8”。很明显,字符串是用utf保存的,但是有没有什么启发式的方法使用符号映射来分析字符并匹配正确的编码?

使用常用编码检测库:

- enca (aptitude install enca)
- chardet (aptitude install chardet)
- uchardet (aptitude install uchardet)
- http://tika.apache.org/
- http://npmjs.com/package/detect-encoding
- libencode-detect-perl
- http://www-archive.mozilla.org/projects/intl/UniversalCharsetDetection.html
- http://jchardet.sourceforge.net/
- http://grepcode.com/snapshot/repo1.maven.org/maven2/com.googlecode.juniversalchardet/juniversalchardet/1.0.3/
- http://lxr.mozilla.org/seamonkey/source/extensions/universalchardet/src/
- http://userguide.icu-project.org/
- http://site.icu-project.org

【问题讨论】:

  • 具体情况还不清楚。您的初始字符串“áÞåàÐÝØÒ..”显然是由于使用错误的编码误解了字节块的结果。因此,不清楚 "The encoding is "ISO8859-5"" 应该是什么意思。任何启发式工具都不会分析“字符”,它需要查看 字节,尝试所有可能的编码,然后确定哪种编码最有可能适合这些字节,因为字符解释似乎最具有统计意义。

标签: encoding utf-8 character-encoding


【解决方案1】:

您需要解开 UTF-8 编码并然后将其传递给字符编码检测库。

如果将随机 8 位数据编码为 UTF-8(假设一个恒等映射,即假设一个 C4 字节表示 U+00C4,就像 ISO-8859-1 及其超集 Windows 1252 的情况一样),你最终会得到类似的东西

Source:  8F    0A 20 FE    65
Result:  C2 8F 0A 20 C3 BE 65

(因为U+008F的UTF-8编码是C2 8F,而U+00FE是C3 BE)。您需要还原此编码以获取源字符串,以便您可以识别其字符编码。

在 Python 中,类似

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import chardet

mystery = u'áÞåàÐÝØÒ ÜÝÞÓÞ ßàØÛÞÖÕÝØÙ Java, ÜÞÖÝÞ ×ÐÝïâì Òáî ÔÞáâãßÝãî ßÐÜïâì'
print chardet.detect(mystery.encode('cp1252'))

结果:

{'confidence': 0.99, 'encoding': 'ISO-8859-5'}

在 Unix 命令行上,

vnix$ echo 'áÞåàÐÝØÒ ÜÝÞÓÞ ßàØÛÞÖÕÝØÙ Java, ÜÞÖÝÞ ×ÐÝïâì Òáî ÔÞáâãßÝãî ßÐÜïâì' |
> iconv -t cp1252 | chardet
<stdin>: ISO-8859-5 (confidence: 0.99)

iconv -t cp1252 file | chardet 解码文件并将其传递给chardet

(为了在命令行上成功运行,您需要正确设置环境以进行透明的 Unicode 处理。我假设您的外壳、终端和语言环境已充分配置。尝试最近的 Ubuntu Live如果您的常规环境停留在 20 世纪,CD 或其他东西。)

在一般情况下,您无法知道错误应用的编码是CP 1252,但在实践中,我猜大多数时候它会是正确的(例如,在这种情况下会产生正确的结果)。在最坏的情况下,您必须遍历所有可用的旧式 8 位编码并全部尝试,然后查看来自chardet 的置信度最高的那些。那么,上面的例子也会更加复杂——从传统的 8 位数据到 UTF-8 的映射将不再是一个简单的身份映射,而是还涉及一个转换表(例如,一个字节 F5 可能任意对应于 U+0092 或其他)。

(顺便说一句,iconv -l 会吐出一长串别名,因此如果您将其用作输入,您将获得许多基本相同的结果。但这里有一个快速的临时尝试来修复您有点奇怪的 Perl脚本。

#!/bin/sh
iconv -l |
grep -F -v -e UTF -e EUC -e 2022 -e ISO646 -e GB2312 -e 5601 |
while read enc; do
    echo 'áÞåàÐÝØÒ ÜÝÞÓÞ ßàØÛÞÖÕÝØÙ Java, ÜÞÖÝÞ ×ÐÝïâì Òáî ÔÞáâãßÝãî ßÐÜïâì' |
    iconv -f utf-8 -t "${enc%//}" 2>/dev/null |
    chardet | sed "s%^[^:]*%${enc%//}%"
done |
grep -Fwive ascii -e utf -e euc -e 2022 -e None |
sort -k4rn

输出仍然包含很多谷壳,但一旦你删除它,判断就很简单了。

在这种情况下尝试任何多字节编码(例如 UTF-16、ISO-2022、GB2312、EUC_KR 等)都是没有意义的。如果您成功地将字符串转换为其中之一,那么结果肯定会是 in 该编码。这超出了上述问题的范围:使用错误的转换表将字符串从 8 位编码转换为 UTF-8。

返回ascii 的肯定做错了什么;他们中的大多数将收到一个空输入,因为iconv 因错误而失败。在 Python 脚本中,错误处理会更直接。)

【讨论】:

  • 非常好!谢谢!把结果贴在下面一点。实际上,在遍历所有可用的 iconv 编码时,我看到了有趣的结果。
  • $ iconv -l | tr "//" " " | perl -ne 'chomp $_; `echo "áÞåàÐÝØÒ ÜÝÞÓÞ ßàØÛÞÖÕÝØÙ Java, ÜÞÖÝÞ ×ÐÝïâì Òáî ÔÞáâãßÝãî ßÐÜïâì" | iconv -t $_ | chardet` =~ /&lt;stdin&gt;:\s*([^\s]+)\s*\(confidence:\s*([\d\.]+)\)/; if($1 &amp;&amp; $2) { print "$2\t$1\n"; }' 2&gt;/dev/null | sort | uniq | sort -k 1,1 -n -r | head -n 10
  • 1.00 UTF-32LE 1.00 UTF-16LE 1.00 ascii 0.99 utf-8 0.99 ISO-8859-5 0.99 ISO-2022-KR 0.99 ISO-2022-JP 0.99 IBM866 0.99 EUC-TW 0.68 ISO-8859 -7
  • 尝试转换echo 'ÀÐÑÞâÐ' | iconv -f ISO-8859-5 -t utf-8 获取УУУУУЂУ 必须是Работа
  • 嗯,是的,如果您将字符串转换为 EUC_KR,那么结果肯定会成为一个 EUC_KR 字符串。您应该限制为 8 位编码(无论如何,首先可以更可靠地识别多字节编码)。我会更新答案以澄清这一点。
【解决方案2】:

字符串

сохранив много приложений Java, можно занять всю доступную память

在 ISO8859-5 中编码为字节

E1 DE E5 E0 D0 DD D8 D2 20 DC DD DE D3 DE 20 DF E0 D8 DB DE D6 D5 DD D8 D9 20 4A 61 76 61 2C 20 DC DE D6 DD DE 20 D7 D0 DD EF E2 EC 20 D2 E1 EE 20 D4 DE E1 E2 E3 DF DD E3 EE 20 DF D0 DC EF E2 EC

字符串

áÞåàÐÝØÒ ÜÝÞÓÞ ßàØÛÞÖÕÝØÙ Java, ÜÞÖÝÞ ×ÐÝïâì Òáî ÔÞáâãßÝãî ßÐÜïâì

以 ISO-8859-1 编码为字节

E1 DE E5 E0 D0 DD D8 D2 20 DC DD DE D3 DE 20 DF E0 D8 DB DE D6 D5 DD D8 D9 20 4A 61 76 61 2C 20 DC DE D6 DD DE 20 D7 D0 DD EF E2 EC 20 D2 E1 EE 20 D4 DE E1 E2 E3 DF DD E3 EE 20 DF D0 DC EF E2 EC

看起来很眼熟?它们是相同的字节,只是被不同的字符集解释不同。

任何查看这些字节的工具都无法自动告诉您字符集,因为它们在两个字符集中都是完全有效的字节。您必须告诉工具在解释字节时使用哪个字符集。

任何告诉您这个特定字节序列被编码为 UTF-8 的工具都是错误。这些不是有效的 UTF-8 字节。

【讨论】:

  • 这不是我理解问题的方式。我的解释是文本被错误地从 ISO-8859-1 编码为 UTF-8,尽管源编码(显然)不是 ISO-8859-1,最终以伪造的 UTF-8 文本结束。
猜你喜欢
  • 2010-12-17
  • 1970-01-01
  • 2012-07-24
  • 2010-09-29
  • 2014-03-24
  • 1970-01-01
  • 2012-03-07
  • 1970-01-01
  • 2013-02-20
相关资源
最近更新 更多