与Java代码点之间的表情符号

问题描述 投票:3回答:1

[在我正在创建的混合Android / Cordova游戏中,我让用户以表情符号+字母数字的形式提供标识符-即0..9,A..Z,a..z-名称。例如

🙋️️Stackoverflow

在服务器端,用户标识符与表情符号和名称部分存储在一起,并且仅要求唯一的名称部分是唯一的。游戏有时会显示“联赛桌”,因此用户可以看到他们与其他玩家相比表现如何。为此,服务器会发回包含表情符号,名称和分数的十个“高分数”值的序列。

然后将其显示在用户的表格中,该表格分为三列-每一列代表表情符号,名称和分数。这就是我遇到的一个小问题。最初,我很天真地认为只要看一下handle.codePointAt(0)就可以弄出表情符号。当我想到表情符号实际上可以是一个或多个16位Unicode值的序列时,我如下更改了代码]

Part 1:剖析用户提供的“句柄”

var i,username,
    codepoints = [], 
    handle = "🙋‍️StackOverflow",
    len = handle,length; 

 while ((i < len) && (255 < handle.codePointAt(i))) 
 {codepoints.push(handle.codePointAt(i));i += 2;}

 username = handle.substring(codepoints.length + 1);

至此,我已经有了[dissected]句柄,并带有

 codepoints =  [128587, 8205, 65039];
 username = 'Stackoverflow;

以上i += 2handle.length的使用说明。 This article建议

  • handle.codePointAt(n)将返回完整的代理对的代码点,如果您遇到前导代理。在我的情况下,由于表情符号必须是第一个字符,因此表情符号的16位Unicode序列的主要替代符号位于0,2,4...
  • 从同一篇文章中,我了解到Javascript中的String.length将返回16位代码单元的数量

第二部分-重新为“联赛桌”生成表情符号

假设我的服务器向应用返回的联赛表格数据的表情符号字符🙇‍️的条目{emoji: [128583, 8205, 65039],username:"Stackexchange",points:100}。现在,这很麻烦。如果我这样做

var origCP = [],
    i = 0, 
    origEmoji = '🙇‍️',
    origLen = origEmoji.length;

    while ((i < origLen) && (255 < origEmoji.codePointAt(i)) 
    {origCP.push(origEmoji.codePointAt(i);i += 2;}

我知道

 origLen = 5, origCP = [128583, 8205, 65039]

但是,如果我从提供的数据中重新生成了表情符号

 var reEmoji = String.fromCodePoint.apply(String,[128583, 8205, 65039]),
     reEmojiLen = reEmoji.length;

我知道

reEmoji = '🙇‍️' 
reEmojiLen = 4;

因此,尽管reEmoji具有正确的表情符号,但其报告的长度却神秘地缩小为4个代码单元,而不是原来的5个。

如果然后从重新生成的表情符号中提取代码点

var reCP = [],
    i = 0;

while ((i < reEmojiLen) && (255 < reEmoji.codePointAt(i)) 
{reCP.push(reEmoji.codePointAt(i);i += 2;} 

这给了我

 reCP =  [128583, 8205];

即使是好奇者,origEmoji.codePointAt(3)也会给出9794的尾随代理对值,而reEmoji.codePointAt(3)会给出下一个完整代理对65039的值。

我现在只能说

我真的在乎吗?

毕竟,我只想在单独的列中显示联赛表格的表情符号,只要我能获得正确的表情符号,引擎盖下发生的事情的细微差别就无关紧要。但是,这很可能会积蓄未来的问题。

这里有人可以说明正在发生的事情吗?

javascript emoji utf-16 surrogate-pairs
1个回答
2
投票

表情符号比单字符更复杂,它们以“顺序”出现,例如zwj序列(将多个表情符号组合到一张图像中)或演示序列(提供同一符号的不同变体)等等,有关更多讨厌的细节,请参阅tr51

如果您像这样“转储”您的字符串

str = "🙋‍️StackOverflow"

console.log(...[...str].map(x => x.codePointAt(0).toString(16)))

您将看到它实际上是一个呈现序列中包装的(不正确格式的)zwj序列。

因此,要准确地切出表情符号,您需要将字符串作为代码点(而不是单位!)数组进行迭代,并提取平面1 CP(> 0xffff)+ ZWJ的+变体选择器。示例:

function sliceEmoji(str) {
    let res = ['', ''];

    for (let c of str) {
        let n = c.codePointAt(0);
        let isEmoji = n > 0xfff || n === 0x200d || (0xfe00 <= n && n <= 0xfeff);
        res[1 - isEmoji] += c;
    }
    return res;
}

function hex(str) {
    return [...str].map(x => x.codePointAt(0).toString(16))
}

myStr = "🙋‍️StackOverflow"

console.log(sliceEmoji(myStr))
console.log(sliceEmoji(myStr).map(hex))
© www.soinside.com 2019 - 2024. All rights reserved.