如何计算 javascript 中带有表情符号的字符串的正确长度?
How to count the correct length of a string with emojis in javascript?
我有点问题。
我正在使用 NodeJS 作为后端。现在,用户有一个字段 "biography",用户可以在其中写一些关于他自己的内容。
假设这个字段有 220 个最大长度,并假设这是输入:
♀️♀️♀️♀️♀️♀️⚕️⚕️
如您所见,没有 220 个表情符号(有 37 个表情符号),但如果我在我的 nodejs 服务器中有
console.log(bio.length)
其中 bio 是输入文本,我得到 221。我如何 "parse" 输入字符串以获得正确的长度?是unicode的问题吗?
已解决
我使用了这个库:https://github.com/orling/grapheme-splitter
我试过了:
var Grapheme = require('grapheme-splitter');
var splitter = new Grapheme();
console.log(splitter.splitGraphemes(bio).length);
而且长度是37,效果很好!
str.length
给出 UTF-16 单位的计数。
以代码点(以字符为单位)获取字符串长度的 Unicode 证明方法是 [...str].length
,因为可迭代协议将字符串拆分为代码点。
如果我们需要字素(字素簇)中的长度,我们有这些本地方法:
一个。 Unicode 属性 在 RegExp 中转义。参见示例:Unicode-aware version of \w or Matching emoji.
b。 Intl.Segmenter — 即将推出,可能在 ES2021 中。可以在最新的 V8 版本中使用标志进行测试(实现与 V8 86 中的最新规范同步)。在 V8 87 中未标记(已发货)。
另请参阅:
从下面的例子可以看出,这是与unicode编码有关,
有一些很棒的资源,例如我从中获取此示例的资源。
https://blog.jonnew.com/posts/poo-dot-length-equals-two
console.log("❤️".length === 11);
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;
}
TL;DR 有解决方案,但并非在所有情况下都有效。 Unicode 感觉像是一门黑暗艺术。
我看到的各种解决方案似乎都存在局限性,问题超出了表情符号并涵盖了 Unicode 范围内的其他字符。如果使用 combing characters. This can even lead to two strings that look the same not being equal,考虑 é 可以存储为 é 或 e + ‘。另请注意,在某些情况下,单个表情符号在存储时可以是 11 个字符,因此假设为 UTF16,则为 22 个字节。
处理方式以及字符的组合或显示方式甚至会因浏览器和操作系统而异。因此,虽然您可能认为自己破解了它,但存在另一个环境破坏它的风险。一定要测试重要的地方。
现在,存在前端与后端问题:您解决了字符数问题,因此它对人类用户来说效果很好,现在您的单个表情符号正好超过了数据库中分配的字段大小。 mongo 等数据库的问题不大,但 SQL 数据库可能是一个问题,其中字段分配是保守的。这意味着您如何解决问题将取决于最困难的限制。
请注意,基本解决方案涉及将字符串转换为数组并获取长度,接受限制:
Array.from(str)
这将在字符组合和处理 astral planes 时分崩离析。
一些考虑到局限性的高级方法:
- 尽可能使用解决前端问题的方法,然后确保解决存储问题
- 如果无法调整数据库或其他存储,请对宣传的前端限制更加保守
- 限制可以输入的字符类型
- 明确指出长度计算的局限性
此外,鉴于问题的复杂性,可能值得看看是否有一个流行的 JS 库已经处理了这个问题?在撰写本文时我没有找到。希望这会在某个时候成为 Javascript 的核心内容。
其他要阅读的页面:
我回答过类似的问题here
但基本上,就是这样:
''.match(/./gu).length == 1
如:
''.length == 2
我原来的更精确 post
我建议使用 runes 包来完成正确的多字节字符串转换,否则如果使用 reducers 和更多的东西来反转字符串,你会遇到更多问题。
看看这个很棒的小包裹:runes
我有点问题。
我正在使用 NodeJS 作为后端。现在,用户有一个字段 "biography",用户可以在其中写一些关于他自己的内容。
假设这个字段有 220 个最大长度,并假设这是输入:
♀️♀️♀️♀️♀️♀️⚕️⚕️
如您所见,没有 220 个表情符号(有 37 个表情符号),但如果我在我的 nodejs 服务器中有
console.log(bio.length)
其中 bio 是输入文本,我得到 221。我如何 "parse" 输入字符串以获得正确的长度?是unicode的问题吗?
已解决
我使用了这个库:https://github.com/orling/grapheme-splitter
我试过了:
var Grapheme = require('grapheme-splitter');
var splitter = new Grapheme();
console.log(splitter.splitGraphemes(bio).length);
而且长度是37,效果很好!
str.length
给出 UTF-16 单位的计数。以代码点(以字符为单位)获取字符串长度的 Unicode 证明方法是
[...str].length
,因为可迭代协议将字符串拆分为代码点。如果我们需要字素(字素簇)中的长度,我们有这些本地方法:
一个。 Unicode 属性 在 RegExp 中转义。参见示例:Unicode-aware version of \w or Matching emoji.
b。 Intl.Segmenter — 即将推出,可能在 ES2021 中。可以在最新的 V8 版本中使用标志进行测试(实现与 V8 86 中的最新规范同步)。在 V8 87 中未标记(已发货)。
另请参阅:
从下面的例子可以看出,这是与unicode编码有关,
有一些很棒的资源,例如我从中获取此示例的资源。
https://blog.jonnew.com/posts/poo-dot-length-equals-two
console.log("❤️".length === 11);
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;
}
TL;DR 有解决方案,但并非在所有情况下都有效。 Unicode 感觉像是一门黑暗艺术。
我看到的各种解决方案似乎都存在局限性,问题超出了表情符号并涵盖了 Unicode 范围内的其他字符。如果使用 combing characters. This can even lead to two strings that look the same not being equal,考虑 é 可以存储为 é 或 e + ‘。另请注意,在某些情况下,单个表情符号在存储时可以是 11 个字符,因此假设为 UTF16,则为 22 个字节。
处理方式以及字符的组合或显示方式甚至会因浏览器和操作系统而异。因此,虽然您可能认为自己破解了它,但存在另一个环境破坏它的风险。一定要测试重要的地方。
现在,存在前端与后端问题:您解决了字符数问题,因此它对人类用户来说效果很好,现在您的单个表情符号正好超过了数据库中分配的字段大小。 mongo 等数据库的问题不大,但 SQL 数据库可能是一个问题,其中字段分配是保守的。这意味着您如何解决问题将取决于最困难的限制。
请注意,基本解决方案涉及将字符串转换为数组并获取长度,接受限制:
Array.from(str)
这将在字符组合和处理 astral planes 时分崩离析。
一些考虑到局限性的高级方法:
- 尽可能使用解决前端问题的方法,然后确保解决存储问题
- 如果无法调整数据库或其他存储,请对宣传的前端限制更加保守
- 限制可以输入的字符类型
- 明确指出长度计算的局限性
此外,鉴于问题的复杂性,可能值得看看是否有一个流行的 JS 库已经处理了这个问题?在撰写本文时我没有找到。希望这会在某个时候成为 Javascript 的核心内容。
其他要阅读的页面:
我回答过类似的问题here
但基本上,就是这样:
''.match(/./gu).length == 1
如:
''.length == 2
我原来的更精确 post
我建议使用 runes 包来完成正确的多字节字符串转换,否则如果使用 reducers 和更多的东西来反转字符串,你会遇到更多问题。
看看这个很棒的小包裹:runes