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

时间:2019-01-25 16:45:44

标签: javascript node.js emoji

我有一个小问题。

我正在使用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 个答案:

答案 0 :(得分:3)

str.length给出UTF-16单位的数量。由于可迭代协议将字符串拆分为代码点,因此以代码点(以字符为单位)获取字符串长度的Unicode验证方法为[...str].length

另请参阅:

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

What every JavaScript developer should know about Unicode

JavaScript has a Unicode problem

Unicode-aware regular expressions in ES2015

ES6 Strings (and Unicode, ❤) in Depth

答案 1 :(得分:1)

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;
}

答案 2 :(得分:1)

TL; DR有解决方案,但并非在每种情况下都有效。 Unicode感觉像是一门黑暗的艺术。

我看到的各种解决方案似乎都存在局限性,这个问题不仅限于表情符号,还涉及Unicode范围内的其他字符。如果使用combing characters,则考虑é可以存储为é或e +′。这甚至可能导致两个看起来相同的not being equal字符串。还要注意,在某些情况下,单个表情符号在存储时可以为11个字符,因此假设为UTF16,则为22个字节。

这种处理方式以及字符组合或显示的方式甚至可能在浏览器和操作系统之间有所不同。因此,尽管您可能认为自己已将其破解,但存在另一种环境可能破坏该漏洞的风险。请务必测试重要的地方。

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

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

Array.from(str)

当字符组合并处理astral planes时,这将崩溃。

一些高级方法,其中考虑到了局限性:

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

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

其他要阅读的页面:

答案 3 :(得分:1)

我回答了类似的问题here

但是基本上,这里是:

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

为:

'?'.length == 2

我的原始帖子更加精确

答案 4 :(得分:0)

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

有很多很棒的资源,例如我从这个例子中借鉴的资源。

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

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

答案 5 :(得分:0)

我建议使用 runes 包来完成正确的多字节字符串转换,否则你会遇到更多问题,例如如果使用 reducer 和更多来反转字符串。

看看这个很棒的小包:runes