我需要处理大量的短字符串列表(大多数是俄语,但是任何其他语言都是可能的,包括来自猫在键盘上行走的随机垃圾)。
其中一些字符串将以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()
电话。也许这可能会有所帮助?
答案 0 :(得分:6)
原则上你不能,特别是允许猫垃圾。
在UTF-8编码一次或两次之前,您没有说明数据的原始字符编码是什么。我假设CP1251,(或至少CP1251是其中一种可能性)因为它是一个非常棘手的案例。
取一个非ASCII字符。 UTF-8编码。你得到一些字节,所有这些字节都是CP1251中的有效字符,除非其中一个碰巧是0x98,这是CP1251中唯一的漏洞。
因此,如果将这些字节从CP1251转换为UTF-8,结果与正确UTF-8编码由这些俄语字符组成的CP1251字符串完全相同。没有办法判断结果是对一个字符进行了不正确的双重编码,还是正确地对两个字符进行了单一编码。
如果您对原始数据有一定的控制权,则可以在其开头放置BOM。然后,当它返回给您时,检查初始字节以查看您是否具有UTF-8 BOM,或者错误地对BOM进行双重编码的结果。但我想你可能对原始文本没有那种控制权。
在练习中你可以猜测 - UTF-8解码它然后:
(a)查看字符频率,字符对频率,不可打印字符的数量。这可能允许您暂时声明它是无意义的,因此可能是双重编码的。有了足够的不可打印的字符,它可能是如此荒谬,以至于你甚至不能通过在键盘上捣碎来实际输入它,除非你的ALT键被卡住了。
(b)尝试第二次解码。也就是说,从通过解码UTF-8数据获得的Unicode代码点开始,首先将其编码为CP1251(或其他),然后从UTF-8解码结果。如果任一步骤失败(由于无效的字节序列),那么它肯定不是双重编码的,至少不使用CP1251作为错误的解释。
如果您有一些可能是UTF-8或可能是CP1251的字节,这或多或少都是您所做的,而您不知道哪个。
对于双编码数据无法区分的单编码cat-garbage会产生一些误报,对于双重编码的数据可能只有很少的误报,但是在第一次编码之后仍然看起来像俄罗斯
如果您的原始编码中有比CP1251更多的漏洞,那么您的漏报就会更少。
字符编码很难。
答案 1 :(得分:4)
这是一个适合我的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/
答案 2 :(得分:0)
class InputGroup extends React.Component {
state = { saveCareersUrlVisible: false } // initial state
//arrow function
inputChangedHandler = (event) => {
if (event.target.type == 'checkbox') {
alert('new value : ' + event.target.checked);
this.setState({ saveCareersUrlVisible: event.target.checked });
} else {
alert('new value : ' + event.target.value);
}
}
render() {
const types = { "Text": 'text', "Date": 'date', "Numeric": "number", "CheckBox": "checkbox" }
return (
//The props.value relates to JSX attribute names for the inputgroup below
<div className="input-groupDynamic">
<label className="labelD" htmlFor={this.props.label.replace(" ", "-")}>{this.props.label}</label>
<input name={this.props.label.replace(" ", "-")}
type={types[this.props.type]}
//uncontrolled components code
//defaultValue={props.value}
//defaultChecked={props.value}
//controlled components code
value={this.props.value}
checked={this.props.value}
onChange={this.inputChangedHandler.bind(this)}
/>
</div>
)
}
}