2011-02-17 77 views
5

我需要处理短字符串的大名单(主要在俄罗斯,但任何其他语言是可能的,包括从猫走在键盘上随机垃圾)。检测一个字符串是双重编码的UTF-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双编码? ? – 2011-02-17 17:50:44

+2

@Martin:FWIW,我的答案假设它意味着在Encoding X中将文本转换为UTF-8字节,然后将这些字节重新解释为Encoding X并将其转换为UTF-8字节。换句话说,当你错误地将UTF-8文件解释为ISO-8859-1(或其他),然后“将其转换为UTF-8”时会发生什么。 – 2011-02-17 18:04:07

+0

那么,原始文本是UTF-8,它又一次被buggy客户端库编码为UTF-8。 (我会尝试获取有关该库假定文本所在编码的更多详细信息。) – 2011-02-17 18:37:10

回答

4

原则上你不能,特别是考虑到猫的垃圾。

你不说什么数据的原始字符编码以前,这是UTF-8编码一次或两次。我会假定CP1251,(或者至少CP1251是其中一种可能性),因为这是一个相当棘手的情况。

取非ASCII字符。 UTF-8编码它。你会得到一些字节,并且所有这些字节在CP1251中都是有效的字符,除非其中一个恰好是0x98,这是CP1251中唯一的漏洞。

所以,如果你转换这些字节从CP1251为UTF-8,结果是完全一样的,如果你正确的UTF-8编码,包括那些俄罗斯字符的字符串CP1251。无法判断结果是由于错误地对一个字符进行了双重编码,还是无法正确地单编码2个字符。

如果你有超过原始数据的一些控制,你可以在它开始把BOM。然后,当它返回给您时,请检查初始字节以查看您是否拥有UTF-8 BOM,或者错误地对BOM重复编码的结果。但是我想你可能对原文没有这种控制。

在实践中你可以猜到 - UTF-8解码,然后:

(一)看字的频率,字符对频率的非打印字符数。这可能会让你暂时宣布它是无意义的,因此可能会进行双重编码。如果有足够的非打印字符,它可能是如此荒谬,以至于即使通过在键盘上捣碎,您也无法真实地输入它,除非您的ALT键被卡住了。

(b)尝试第二次解码。也就是说,从通过解码UTF-8数据获得的Unicode代码点开始,首先将其编码为CP1251(或其他),然后解码UTF-8的结果。如果任一步骤失败(由于字节序列无效),那么它肯定不是双重编码的,至少不使用CP1251作为错误解释。

这是多还是少,如果你有一些字节,这可能是UTF-8或可能是CP1251你做什么,你不知道哪个。

对于单编码的猫垃圾和双编码数据难以区分的情况,您可能会得到一些误报,对于双编码但第一次编码后仍然看起来像的数据,俄语。

如果您的原始编码比CP1251有更多的漏洞,那么您的漏报次数会更少。

字符编码很难。

2

这是一个适用于我的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/