【问题标题】:Why is "????".length === 2?为什么是“????”.length === 2?
【发布时间】:2016-11-15 16:03:25
【问题描述】:

我的浏览器中的任何文本区域如何处理看似 2 个表示为一个字符的字符?

例如:

"????".length
// -> 2

这里有更多示例: https://jsbin.com/zazexenigi/edit?js,console

【问题讨论】:

标签: javascript utf-8 emoji


【解决方案1】:

Javascript 使用UTF-16 (source) 来管理字符串。

在 UTF-16 中有 1,112,064 个可能的字符。现在,每个字符都使用code points 来表示(*)。在 UTF-16 中,一个代码点使用两个字节(16 位)来保存。这意味着使用一个代码点,您可以拥有 65536 个不同的字符。

这意味着某些字符必须用两个代码点表示。

String.length() 返回字符串中的代码单元数,而不是字符数。

MDN很好地解释了页面上关于String.length()的事情

此属性返回字符串中的代码单元数。 JavaScript 使用的字符串格式 UTF-16 使用一个 16 位的代码单元来表示最常见的字符,但对于不常用的字符需要使用两个代码单元,所以长度返回的值可能为与字符串中的实际字符数不匹配。

(*):实际上,在 010000 – 03FFFF040000 – 10FFFF 范围内的某些字符每个代码点最多可以使用 4 个字节(32 位),但是这不会改变答案:有些字符需要超过 2 个字节才能表示,因此它们需要超过 1 个代码点。

这意味着一些需要超过 16 位的字符的长度无论如何都是 1。和0x03FFFF一样,需要21位,但在UTF-16中只使用了一个编码单元,所以它的String.length为1。

console.log(String.fromCharCode(0x03FFFF).length)

【讨论】:

  • 我认为只有 ES2015 在引擎内部和语言级别上都使用 UTF-16。 ES5 使用 UCT-2 编码(至少在语言级别上)。此外,每个字符只有一个代码点(从 0x0 到 0x10FFFF)),由一到两个代码单元表示。因为string.length 将代码单元解释为单个字符,所以它会为基本多语言平面 (BMP) 之外的字符计算错误的结果。
  • @LUH3417 afaik ES5 也使用 UTF-16:When a String contains actual textual data, each element is considered to be a single UTF-16 code unit.es5.github.io
  • 哦,我的错。它被称为 UCS-2 和 ES5 引擎可以使用它们中的任何一个(UCS-2/UTF-16)。
  • @LUH3417 请随时改进我的答案:)
  • 不需要。 cmets 完成这项工作。更多关于Unicode in ES2015.
【解决方案2】:

我相信 rpadovani 最好地回答了您的“为什么”问题,但是为了在这种情况下为您提供正确的字形计数的实现,Lodash 在他们的 toArray 模块中解决了这个问题。

例如,

_.toArray('12?').length; // --> 3

或者,如果您想从字符串中剔除几个任意字符,您可以操作并重新加入数组,例如:

_.toArray("?trimToEightGlyphs").splice(0,8).join(''); // --> '?trimToE'

【讨论】:

  • 我可以发誓 lodash 是口袋妖怪的名字。
【解决方案3】:

我找到了一种获得正确结果的简单方法。
这里是:

'?Some text with emojis?'.match(/./gu)

它应该返回:

[ "?","S", "o", "m", "e", " ", "t", "e", "x", "t", " ", "w", "i", "t", "h", " ", "e", "m", "o", "j", "i", "s", "?"]

然后你可以在上面申请.length

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

它使用正则表达式匹配:/./gu

. 匹配任何单个字符。
g mean 'global' : 它基本上允许在第一场比赛后不停止。
u 意思是 'unicode' :它允许以正确的方式显示字符(没有它 ? 将显示为 ��(所以 2 个字符))

顺便说一句,您可以添加 m 以支持多行 (/./gum)

希望对您有所帮助 ?

【讨论】:

  • 不适用于所有情况。例如,"?‍❤️‍?‍?".match(/./gu).length 输出 8
  • 就@BrianK.而言,我相信有些人会寻找如何将其算作“一个”的答案,但这实际上正是我所需要的,因为这些实际上是 8 个字符(粘贴它在支持 Unicode 的测试编辑器中并开始按 Backspace”,你会看到那里有什么!)这正是它在 MySQL 的 varchar 字段中需要多少“字符”。所以如果你想检查它是否在插入之前将适合您的数据库 - 这可能是您想要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-24
  • 1970-01-01
  • 1970-01-01
  • 2010-10-23
  • 2012-04-11
  • 2020-01-23
  • 1970-01-01
相关资源
最近更新 更多