码点、码元与Emoji长度计算

1. 介绍

在计算机科学中,字符的表示和存储涉及到两个重要的概念:代码点(Code Point)代码单元(Code Unit)。它们是在处理文本数据时至关重要的概念。

1.1 码点(Code Point)

  • 定义:码点是Unicode中的一个基本概念,它代表了字符集中的每个字符的唯一位置
  • 表示:码点通常用十六进制表示,并以U+开头,后面跟随一个或多个十六进制数字。例如,U+0041表示拉丁字母“A”
  • 作用:码点是字符在字符集中的抽象位置,它们并不直接表示字符的存储形式或编码方式,而是作为字符的唯一标识。

1.2 码元(Code Unit)

  • 定义:码元是计算机中存储和处理字符的最小单位
  • 特点:一个字符(或码点)可以由一个或多个码元组成,码元是构成编码方案的基本单位。
  • 不同编码方案中的表示:在不同的编码方案中,一个字符可能由不同数量的码元组成。例如,在 UTF-8编码中,一个字符可能由1 ~ 4个字节(8 位)组成,而在UTF-16编码中,一个字符通常由2 个字节(16 位)表示。

1.3 关系

码点与码元之间的关系是:一个字符(码点)可以由一个或多个码元组成,具体取决于使用的编码方案。不同编码方案下的字符编码和解码过程将码点映射到相应的码元序列,从而实现字符在计算机中的存储和处理。

2. codePointAt() 方法介绍

2.1 简介

codePointAt()方法是 JavaScript字符串对象的一个方法,用于返回给定索引处字符的Unicode码点值(十进制数字)。

2.2 语法

str.codePointAt(index)
  • 参数:
    • str:要从中获取字符的字符串。
    • index:一个整数,指定要返回其 Unicode 码点值的字符的索引。如果省略该参数,则默认为 0。
  • 返回值:一个表示指定索引处字符的 Unicode 码点值的整数。
  • 示例
let str = '😊';
let codePoint = str.codePointAt(0);
console.log(codePoint); // 输出 128522,对应 Unicode 码点 U+1F60A

2.3 注意事项

  • 如果指定索引处的字符由两个UTF-16代码单元组成(即代理对),则返回代理对的码点值。
  • 如果索引超出字符串长度范围,则返回undefined
  • 对于不支持该方法的旧版浏览器,可以使用polyfill进行兼容处理。

2.4 使用场景

  • 获取字符串中特定位置字符的Unicode码点值(十进制数字),特别是处理包含Emoji或其他Unicode字符的字符串时。
  • 在字符串处理、字符编码等方面的应用中,用于获取字符的Unicode码点值,进一步进行处理和分析。

3. 包含 Emoji 的字符串的视觉长度计算

Emoji 是一种符号字符,广泛用于表达情感、状态和概念。它们被纳入Unicode标准,以便在不同的平台和设备上进行一致的显示和交流。EmojiUnicode中的位置通常位于较高的码点范围内,这意味着它们的码点值较大。

JavaScript中,要计算包含Emoji的字符串的码点数量,通常可以使用字符串的length属性。然而,需要注意的是,对于包含代理对(surrogate pairs)的Emojilength属性返回的值可能不准确,因为JavaScript中的length属性返回的是字符串中的字符数量,而不是码点数量。

3.1 使用 codePointAt() 计算

字符串中确保每个字符(包括Emoji)都被计算为一个单位,我们需要对codePointAt()返回的结果进行处理。当返回的码点大于 65535时,我们需要将其作为一个字符处理,并且需要额外的操作来跳过低位区的码点。

const getCodePointLength = (str) => {
    let len = 0;
    for (let i = 0; i < str.length;) {
        len++;
        const codePoint = str.codePointAt(i) ?? 0
        i += codePoint > 65535 ? 2 : 1;
    }
    return len;
};
getCodePointLength('123😂abc😀'); // 8

3.2 使用 Array.from() 计算

当你使用Array.from()方法时,它会将字符串中的每个 Unicode 字符(包括 emoji)转换为一个独立的 JavaScript 字符串。因此,如果一个 emoji 由多个 UTF-16 编码的字符组成,Array.from()将会将其拆分为多个独立的 JavaScript 字符串。

const codePointArray = Array.from('123😂abc😀'); // ["1", "2", "3", "😂", "a", "b", "c", "😀"];
codePointArray.length() // 8

3.3 组合 Emoji 表情的长度计算

组合 Emoji 表情的长度计算在实际情况中需要特别注意,因为这些表情通常由多个字符组成,包括实际的表情符号以及连接符。在计算长度时,我们需要注意这些连接符。如果只要求不在截取字符串时,不显示乱码,那么连接符的问题可以忽略。

  • Emoji 表情连接符
    • 零宽度连接器(Zero Width Joiner,ZWJ): ZWJ 的Unicode码点为U+200D(code point 8205)。它主要用于将多个Emoji 字符连接在一起,以创建新的序列。这种连接器在创建人物、家庭和职业等多个 Emoji 组合的情况下很常见。例如,将男性和女性的 Emoji 符号连接在一起以形成一个家庭的图像。

    • 变体选择器-16(Variation Selector-16,VS16): VS16 的 Unicode 码点范围为 U+FE00 至 U+FE0F。它们用于指定与基本 Emoji 字符具有不同样式或变体的图形。例如,VS16 可以用于指定彩色或黑白版本的 Emoji 符号。

例如:

Array.from('abc👩‍👩‍👧‍👧'); // ['a', 'b', 'c', '👩', '‍', '👩', '‍', '👧', '‍', '👧']

其中,数组中的空字符并不是真正的空字符,事实上,它被称为零宽度连接器。不管我们对该数组如何截取,都不会出现乱码的情况,尽管视觉长度不统一。如果严格要求视觉长度的统一,需要研究emoji造字法来进行计算。

附:七种 emoji 造字法

一共有七种 emoji 造字法

  1. 基础 emoji,单个码点表示一个emoji 🧛 U+1F9DB
  2. 单个码点 + 变体选择符 ⚛️ = ⚛︎ U+26A0 + U+FE0F
  3. 皮肤修饰符 🤵🏽 = 🤵 U+1F935 + 🏽 U+1F3FD
  4. ZWJ 连接符 👨‍💻 = 👨 + ZWJ + 💻
  5. 旗帜符号 🇨🇳 = 🇨 + 🇳
  6. 标签序列 🏴󠁧󠁢󠁳󠁣󠁴󠁿 = 🏴 + gbsct + U+E007F
  7. 键位序列 *️⃣ = * + U+FE0F + U+20E3
    前四种方法也可以组合使用,可构造非常复杂的 emoji,例如
    U+1F6B5 🚵 个人山地骑行
    +U+1F3FB 浅色皮肤
    +U+200D ZWJ
    +U+2640 ♀️女性标志
    +U+FE0F 变体标志
    = 🚵🏻‍♀️ 浅色皮肤的女性山地骑行

4. 参考

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值