magicdawn / magicdawn

个人学习 / 代码 / 总结 / 读书笔记
25 stars 3 forks source link

unicode #181

Open magicdawn opened 4 months ago

magicdawn commented 4 months ago
Q: unicode 多码点字符, unicode 与 utf8 是什么关系, 多码点字符在 JavaScript 中是如何处理的, string.length 会有影响么, String.prototype.codePointAt 是取的单码点么 ### Unicode 多码点字符 Unicode 多码点字符指的是那些由多个码点(code points)组成的字符。例如,一些复杂的 emoji 和某些组合字符都是多码点字符。 ### Unicode 与 UTF-8 的关系 Unicode 是一个字符编码标准,涵盖了全球大部分书写系统。UTF-8 是 Unicode 的一种编码方式,将每个 Unicode 码点编码成 1 到 4 个字节。具体关系如下: - Unicode 定义了所有字符及其码点(唯一编号)。 - UTF-8 是一种编码方案,用来在计算机中表示这些 Unicode 码点。 ### 多码点字符在 JavaScript 中的处理 JavaScript 的字符串(String)在处理多码点字符时有一些特殊之处,因为其基本的字符单元是 16 位的 UTF-16 代码单元。 - `string.length` 返回的是字符串中的代码单元的数量,而不是字符的数量。所以对于多码点字符,这个值会比预期的要大。例如,某些 emoji 可能由两个代码单元组成,但它们只是一个字符。 - `String.prototype.codePointAt` 返回的是指定位置的码点,即使这个位置的字符是一个多码点字符。对于一个多码点字符,你需要两个调用才能遍历完这个字符的所有码点。 ### 示例代码 ```javascript let str = '𝄞'; // 一个由两个码点组成的字符 console.log(str.length); // 输出 2 console.log(str.codePointAt(0).toString(16)); // 输出 '1d11e' console.log(str.codePointAt(1)); // 输出 undefined,因为这个位置是第二个码点的一部分 ``` ### 总结 - `string.length` 可能不会如预期般反映字符数量,因为它返回的是代码单元数量。 - `String.prototype.codePointAt` 可以准确获取码点,但需要注意它是针对单个码点的。 - JavaScript 需要额外的处理才能正确遍历和操作多码点字符,例如使用 `for...of` 循环或 `Array.from`。 ### 处理多码点字符的常用方法 ```javascript let str = '𝄞a'; // 一个多码点字符加一个普通字符 // 使用 Array.from 正确遍历字符 for (let char of Array.from(str)) { console.log(char); // 依次输出 𝄞 和 a } // 使用 for...of 语句 for (let char of str) { console.log(char); // 依次输出 𝄞 和 a } // 获取字符串中的字符数量 let charCount = [...str].length; console.log(charCount); // 输出 2 ``` 这样可以确保处理多码点字符时不出错。
Q: '\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F' 即 `'👨🏾‍⚕️'` 的 length 为什么是 7, 不是根据 `\u` 的个数来数的么 在 JavaScript 中,字符串的 `length` 属性返回的是 UTF-16 代码单元的数量,而不是实际字符或码点的数量。理解这一点需要了解一些背景知识: 1. UTF-16 编码: - Unicode 字符可以表示成一个或多个码点(code points)。 - 在 UTF-16 中,码点可以是单个 16 位值(称为基本多文种平面,BMP)或一对 16 位值(称为代理对,surrogate pairs)。 - 代理对是高位代理(high surrogate,范围为 `\uD800-\uDBFF`)和低位代理(low surrogate,范围为 `\uDC00-\uDFFF`)的组合,用于表示超过 `\uFFFF` 的字符。 2. 示例字符的组成: - `'\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F'` 或 `'👨🏾‍⚕️'` 是由多个码点组合而成的字符。 - 具体组成部分如下: - `\u{1F468}`: 👨 (一个代理对,占两个代码单元) - `\u{1F3FE}`: 🏾 (一个代理对,占两个代码单元) - `\u200D`: Zero Width Joiner (ZWJ) (一个代码单元) - `\u2695`: ⚕ (一个代码单元) - `\uFE0F`: Variation Selector-16 (VS16) (一个代码单元) 这些组合在一起形成了一个复杂的 emoji。 ### 计算 `length` 1. 👨 (`\u{1F468}`) - 2 个代码单元 2. 🏾 (`\u{1F3FE}`) - 2 个代码单元 3. ZWJ (`\u200D`) - 1 个代码单元 4. ⚕ (`\u2695`) - 1 个代码单元 5. VS16 (`\uFE0F`) - 1 个代码单元 总共是 7 个代码单元。 ```javascript let complexEmoji = '\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F'; console.log(complexEmoji.length); // 输出 7 ``` ### 总结 - JavaScript 的 `string.length` 返回的是 UTF-16 代码单元的数量,而不是字符或码点的数量。 - 复杂的 emoji 可能由多个码点组成,每个码点可能由一个或两个代码单元表示。 - 在这种情况下,每个 `\u{...}` 代表的码点可能需要两个代码单元(对于代理对),而不是一个代码单元。 要准确计算字符的数量,可以使用 `Array.from` 或 `for...of` 循环,这些方法会正确处理多码点字符: ```javascript console.log(Array.from(complexEmoji).length); // 输出 1 console.log([...complexEmoji].length); // 输出 1 for (let char of complexEmoji) { console.log(char); // 输出 👨🏾‍⚕️ } ```