检测字符串是否以UTF-8双重编码

Ale*_*ysh 7 language-agnostic unicode 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));
Run Code Online (Sandbox Code Playgroud)

注意toLowerCase()电话.也许这可能有帮助吗?

Ste*_*sop 6

原则上你不能,特别是允许猫垃圾.

在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的字节,这或多或少都是您所做的,而您不知道哪个.

对于双编码数据无法区分的单编码cat-garbage,你会得到一些误报,对于双重编码的数据可能只有很少的误报,但是在第一次编码之后仍然看起来像俄语.

如果您的原始编码中有比CP1251更多的漏洞,那么您的漏报就会更少.

字符编码很难.


red*_*ent 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 );
Run Code Online (Sandbox Code Playgroud)

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