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

Stack️Stackoverflow

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

然后将其显示在具有三列的表中,每列分别用于表情符号,名称和分数。这就是我遇到的一个小问题。最初,我很天真地认为我可以通过简单地查看handle.codePointAt(0)来找出表情符号。当我发现表情符号实际上可以是一个或多个16位Unicode值的序列时,我如下更改了代码

第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);

在这一点上,我有“解剖” handle
 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的值。

    我现在只能说



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

    这里的任何人都可以对正在发生的事情有所了解吗?

    最佳答案

    表情符号不仅比单个字符还要复杂,它们以“序列”的形式出现,例如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))

    10-02 15:16
    查看更多