检测 UTF 中的实际字符集编码

Detect actual charset encoding in UTF

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

例如字符串:áÞåàÐÝØÒ ÜÝÞÓÞ ßàØÛÞÖÕÝØÙ 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

字符串

сохранив много приложений 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 字节。

您需要解包 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 处理。我假设您的 shell、终端和区域设置已充分配置. 如果你的常规环境停留在 20 世纪,请尝试最近的 Ubuntu Live CD 或其他东西。)

在一般情况下,您无法知道错误应用的编码是 CP 1252,但在实践中,我猜大多数时候它都是正确的(例如,在这种情况下产生正确的结果) .在最坏的情况下,您将不得不遍历所有可用的传统 8 位编码并尝试所有编码,然后查看 chardet 中具有最高置信度的编码。然后,上面的示例也将更加复杂——从遗留 8 位数据到 UTF-8 的映射将不再是简单的身份映射,而是还涉及翻译 table(例如,字节 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 的字符串 table.

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