检测一个字符串是否是在UTF-8中被双重编码

8

我需要处理一个大量的短字符串列表(主要是俄语,但也可能包括其他语言,包括猫走在键盘上留下的随机垃圾)。

其中一些字符串将被两次编码为UTF-8。

我需要可靠地检测给定字符串是否被双重编码,并进行修复。我应该通过检查字节来完成此操作,而不使用任何外部库。检测应尽可能快速。

问题是:如何检测给定字符串是否已经被编码两次UTF-8?

更新:

原始字符串是UTF-8编码的。以下是执行第二次编码的AS3代码(不幸的是,我无法控制客户端代码,因此无法修复此问题):

private function toUTF8(s : String) : String {
       var byteArray : ByteArray = new ByteArray();
       byteArray.writeUTFBytes(s);
       byteArray.position = 0;

       var res : String = "";

       while(byteArray.bytesAvailable){
           res += String.fromCharCode(byteArray.readUnsignedByte());
       }

       return res;
}

myString = toUTF8(("" + myString).toLowerCase().substr(0, 64));

注意 toLowerCase() 方法的调用。也许这会有所帮助?


1
在UTF8中,双重编码是什么意思? - Martin Beckett
2
@Martin:就我所知,我的答案假设它的意思是将编码为X的文本转换为UTF-8字节,然后取这些字节,重新解释为编码为X并将其转换为UTF-8字节。换句话说,当您错误地将UTF-8文件文件解释为ISO-8859-1(或其他任何格式),然后“将其转换为UTF-8”时会发生什么。 - Steve Jessop
原始文本是UTF-8编码的,但由于有缺陷的客户端库再次对其进行了UTF-8编码。(我将尝试获取有关该库假定文本采用的编码的更多详细信息。) - Alexander Gladysh
3个回答

8
原则上你是不能的,特别是考虑到猫垃圾问题。在它被UTF-8编码一次或两次之前,你没有说明原数据的原始字符编码是什么。我假设是CP1251(或者至少CP1251是其中之一),因为这是一个相当棘手的情况。选择一个非ASCII字符,对其进行UTF-8编码。你会得到一些字节,而所有这些字节在CP1251中都是有效字符,除非其中一个恰好是0x98(CP1251中唯一的洞)。所以,如果你将这些字节从CP1251转换为UTF-8,结果与正确地UTF-8编码由这些俄语字符组成的CP1251字符串的结果完全相同。无法判断结果是从错误地双重编码一个字符还是正确地单编码了2个字符。如果你对原始数据有一定控制权,可以在开头放置BOM。然后当它回到你手中时,检查最初的字节,看看是否有UTF-8 BOM,或者是错误地双重编码的BOM的结果。但我想你可能无法对原始文本进行这种控制。在实践中,你可以猜测——UTF-8解码,然后:(a)查看字符频率、字符对频率、不可打印字符数量。这可能使你能够暂时声明它是无意义的,因此可能是双重编码的。如果有足够多的不可打印字符,它可能是如此无意义,以至于你甚至无法通过在键盘上随便按几个键来实际输入它,除非你的ALT键卡住了。(b)尝试第二次解码。也就是说,从你通过解码UTF-8数据得到的Unicode代码点开始,首先将其编码为CP1251(或其他),然后将结果从UTF-8解码。如果任一步骤失败(由于字节序列无效),那么它肯定没有被双重编码,至少不能使用CP1251作为错误解释。如果你有一些可能是UTF-8或CP1251的字节,并且你不知道哪个是哪个,这就是你要做的事情。你会得到一些单编码猫垃圾无法辨别的双编码数据的误报,以及可能有很少的误报负面数据,这些数据经过第一次编码后仍然看起来像俄语。如果你的原始编码比CP1251更有洞,则误报负面情况会更少。字符编码很难。

你说得对,我无法控制原始字符串。但是我已经更新了问题并提供了更多信息,也许这会有所帮助。 - Alexander Gladysh
BOM编码的UTF-8并没有什么特别之处,它只是将其标记为UTF-8而不是其他任何编码中的垃圾。 - Deduplicator

5
以下是一个对我有效的 PHP 算法。
如果您无法修复数据,以下是一个技巧:
if ( mb_detect_encoding( utf8_decode( $value ) ) === 'UTF-8' ) {
    // Double encoded, or bad encoding
    $value = utf8_decode( $value );
}

$value = \ForceUTF8\Encoding::toUTF8( $value );

我使用的库是:https://github.com/neitanod/forceutf8/

期望输入的错误率是多少? - Deduplicator

0
if (mb_detect_encoding(mb_convert_encoding($string,'Windows-1251','UTF-8'),'Windows-1251,UTF-8',true) === 'UTF-8' ){
  $string=mb_convert_encoding($string,'Windows-1251','UTF-8');
};

预期输入的错误率是多少? - Deduplicator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接