我有一些数据表示为整数数组,最多可以包含20万个元素。整数值可以在0到200 000之间变化。
为了模拟此数据(出于调试目的),我可以执行以下操作:
let data = [];
let len = 200000
for (let i = 0; i < len; i++) {
data[i] = i;
}
要将整数数组转换为unicode字符串,请执行以下操作:
let dataAsText = data.map((e) => {
return String.fromCodePoint(e);
}).join('');
当我想转换回整数数组时,该数组似乎更长:
let dataBack = dataAsText.split('').map((e) => {
return e.codePointAt(e);
});
console.log(dataBack.length);
它怎么来的?怎么了 ?
额外的信息:
我使用codePointAt / fromCodePoint,因为它可以处理所有unicode值(最多21位),而charCodeAt / fromCharCode失败。
例如,使用.join('123')和.split('123')将使变量dataBack与data的长度相同。但这不是一个很好的解决方案,因为字符串dataAsText的大小将不必要地变得很大。
如果let len等于或小于65536(最大值为2 ^ 16或16位),则一切正常。哪个奇怪?
编辑:
我使用codePoint是因为我需要将数据转换为unicode文本,以便数据简短。
有关codePoint与charCode的更多信息,并提供示例:
如果我们将150000转换为字符,则使用codePoint返回为整数:
console.log(String.fromCodePoint("150000").codePointAt(0));
这给了我们150000是正确的。对charCode进行相同操作失败,并显示18928(而不是150000):
console.log(String.fromCharCode("150000").charCodeAt(0));
最佳答案
这是因为较高的代码点值将产生2个字,如以下代码片段所示:
var s = String.fromCodePoint(0x2F804)
console.log(s); // Shows one character
console.log('length = ', s.length); // 2, because encoding is \uD87E\uDC04
var i = s.codePointAt(0);
console.log('CodePoint value at 0: ', i); // correct
var i = s.codePointAt(1); // Should not do this, it starts in the middle of a sequence!
console.log('CodePoint value at 1: ', i); // misleading
在您的代码中,当您执行
split
时,会出错,因为组成字符串的单词都被拆分了,从而放弃了某些对旨在组合成单个字符的事实。您可以对此使用ES6解决方案,其中spread syntax将此考虑在内:
let dataBack = [...dataAsText].map((e, i) => {
// etc.
现在您的数量将是相同的。
例:
// (Only 20 instead of 200000)
let data = [];
for (let i = 199980; i < 200000; i++) {
data.push(i);
}
let dataAsText = data.map(e => String.fromCodePoint(e)).join("");
console.log("String length: " + dataAsText.length);
let dataBack = [...dataAsText].map(e => e.codePointAt(0));
console.log(dataBack);
代孕
请注意,在0 ... 65535范围内,有保留给所谓的surrogates的范围,这些范围仅在与另一个值组合时表示一个字符。您不应迭代那些期望这些值自己代表字符的人。因此,在您的原始代码中,这将是另一个错误来源。
要解决此问题,您实际上应该跳过这些值:
for (let i = 0; i < len; i++) {
if (i < 0xd800 || i > 0xdfff) data.push(i);
}
实际上,还有许多其他代码点不代表字符。