如何在javascript中使用表情符号计算字符串的正确长度?

IT技术 javascript node.js emoji
2021-02-23 01:03:38

我有点问题。

我使用 NodeJS 作为后端。现在,用户有一个字段“传记”,用户可以在其中写一些关于他自己的东西。

假设该字段的最大长度为 220,并将其作为输入:

👶🏻👦🏻👧🏻👨🏻👩🏻👱🏻‍♀️👱🏻👴🏻👵🏻👲🏻👳🏻‍♀️👳🏻👮🏻‍♀️👮🏻👷🏻‍♀️👷🏻💂🏻‍♀️💂🏻🕵🏻‍♀️👩🏻‍⚕️👨🏻‍⚕️👩🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾👨🏻‍🌾 

正如你所看到的,没有 220 个表情符号(有 37 个表情符号),但是如果我在我的 nodejs 服务器中这样做

console.log(bio.length)

bio 是输入文本,我得到 221。如何“解析”字符串输入以获得正确的长度?是unicode的问题吗?

解决了

我使用了这个库:https : //github.com/orling/grapheme-splitter

我试过了:

var Grapheme = require('grapheme-splitter');
var splitter = new Grapheme();
console.log(splitter.splitGraphemes(bio).length);

并且长度是37。效果很好!

6个回答
  1. str.length 给出 UTF-16 单位的计数。

  2. 在代码点(以字符为单位)中获取字符串长度的 Unicode 证明方法是[...str].length可迭代协议将字符串拆分为代码点。

  3. 如果我们需要字素(字素簇)的长度,我们有这些原生方式:

    一个。RegExp 中的 Unicode 属性转义。参见示例:Unicode 感知版本的 \w匹配表情符号

    Intl.Segmenter——即将推出,可能在 ES2021 中。可以在最后一个 V8 版本中使用标志进行测试(实现与 V8 86 中的最后一个规范同步)。在 V8 87 中未标记(发货)。

也可以看看:

这些都没有给出正确的答案 👩‍👩‍👧‍👦
2021-04-21 01:03:38
那么,我们需要定义最大长度应该是什么单位。我们有 221 个 UTF-16 单元、131 个 Unicode 点(字符)或 37 个组合字素。
2021-04-25 01:03:38
如果需要,@R3m[...new Intl.Segmenter().segment('👩‍👩‍👧‍👦')].length会提供1(字素)1
2021-05-09 01:03:38
破坏示例字符串会返回 37 吗?130 对我来说。
2021-05-18 01:03:38
这个问题非常需要输出 37 而不是 130 的代码。[...str].length对于将表情符号作为一个单元进行计数是不正确的。您可能想在答案中澄清这一点,以免给人们带来不必要的麻烦。
2021-05-18 01:03:38

TL;DR 有解决方案,但它们并不适用于所有情况。Unicode 感觉像是一门黑暗的艺术。

我所看到的各种解决方案似乎都有局限性,问题超出了表情符号并涵盖了 Unicode 范围内的其他字符。考虑 é 可以存储为 é 或 e + ',如果使用组合字符这甚至会导致两个看起来相同的字符串不相等另请注意,在某些情况下,单个表情符号在存储时可能为 11 个字符,因此假设为 UTF16,则为 22 个字节。

这种处理方式以及字符的组合或显示方式甚至会因浏览器和操作系统而异。因此,虽然您可能认为自己破解了它,但存在另一个环境破坏它的风险。一定要测试重要的地方。

现在,存在前端与后端问题:您解决了字符计数问题,因此它对人类用户很有效,现在您的单个表情符号超出了数据库中分配的字段大小。mongo 等数据库的问题较少,但 SQL 数据库可能是一个问题,其中字段分配是保守的。这意味着您如何解决问题将取决于最困难的限制在哪里。

请注意,基本解决方案确实涉及将字符串转换为数组并获取长度,接受限制:

Array.from(str)

这将在角色组合和处理星界时分崩离析

一些考虑到局限性的高级方法:

  • 尽可能使用解决前端问题的方法,然后确保解决存储问题
  • 如果无法调整数据库或其他存储,则对宣传的前端限制更加保守
  • 限制可以输入的字符类型
  • 明确指出长度计算的限制

此外,考虑到问题的复杂性,是否有一个流行的 JS 库已经处理过这个问题可能值得一看?在撰写本文时我没有找到。希望这会在某个时候成为 Javascript 的核心。

其他需要阅读的页面:

在这里回答了一个类似的问题

但基本上,这里是:

'👍'.match(/./gu).length == 1

作为 :

'👍'.length == 2

我原来的帖子更精确

是的,它并不完美,但仍然比 "👩‍❤️‍💋‍👩".length
2021-04-25 01:03:38
不适用于所有情况。例如,"👩‍❤️‍💋‍👩".match(/./gu).length输出8
2021-05-11 01:03:38
function fancyCount2(str){
  const joiner = "\u{200D}";
  const split = str.split(joiner);
  let count = 0;

  for(const s of split){
    //removing the variation selectors
    const num = Array.from(s.split(/[\ufe00-\ufe0f]/).join("")).length;
    count += num;
  }

  //assuming the joiners are used appropriately
  return count / split.length;
}
我发誓我之前没有读过那篇文章,我只是想自己使用正则表达式,但仍然是必读的文章
2021-04-23 01:03:38
最好归功于您从中获得该帖子的帖子blog.jonnew.com/posts/poo-dot-length-equals-two该帖子解释了它的工作原理,并指出在某些情况下它不起作用.
2021-05-01 01:03:38

从下面的示例中可以看出,这与 unicode 编码有关,

有一些很棒的资源,例如我从这个示例中获取的资源。

https://blog.jonnew.com/posts/poo-dot-length-equals-two

console.log("👩‍❤️‍💋‍👩".length === 11);

我读了那个并测试了fancycount函数,它对我不起作用..我也读到了fancycount2版本。
2021-05-11 01:03:38