2022 年 8 月 5 日

Unicode:使用标志“u”和类\p{...}。

JavaScript 使用 Unicode 编码 来表示字符串。大多数字符使用 2 个字节进行编码,但这最多只能表示 65536 个字符。

这个范围不足以编码所有可能的字符,因此一些罕见的字符使用 4 个字节进行编码,例如 𝒳(数学 X)或 😄(微笑),一些象形文字等等。

以下是某些字符的 Unicode 值

字符 Unicode Unicode 中的字节计数
a 0x0061 2
0x2248 2
𝒳 0x1d4b3 4
𝒴 0x1d4b4 4
😄 0x1f604 4

因此,像 a 这样的字符占用 2 个字节,而 𝒳𝒴😄 的代码更长,它们有 4 个字节。

很久以前,当 JavaScript 语言被创建时,Unicode 编码更简单:没有 4 字节字符。因此,一些语言特性仍然无法正确处理它们。

例如,length 认为这里有两个字符

alert('😄'.length); // 2
alert('𝒳'.length); // 2

…但我们可以看到只有一个,对吧?关键是 length 将 4 个字节视为两个 2 字节字符。这是不正确的,因为它们必须被视为一个整体(所谓的“代理对”,您可以在文章 字符串 中阅读有关它们的更多信息)。

默认情况下,正则表达式也将 4 字节的“长字符”视为一对 2 字节字符。并且,正如字符串中发生的那样,这可能会导致奇怪的结果。我们将在稍后的文章 集合和范围 [...] 中看到这一点。

与字符串不同,正则表达式有标志 u 来解决这些问题。使用此标志,正则表达式可以正确处理 4 字节字符。并且 Unicode 属性搜索也变得可用,我们将在下一节中介绍它。

Unicode 属性 \p{…}

Unicode 中的每个字符都有许多属性。它们描述了字符属于哪个“类别”,包含有关它的各种信息。

例如,如果一个字符具有 Letter 属性,则表示该字符属于一个字母表(任何语言的字母表)。而 Number 属性表示它是一个数字:可能是阿拉伯数字或汉字,等等。

我们可以搜索具有特定属性的字符,写成 \p{…}。要使用 \p{…},正则表达式必须具有标志 u

例如,\p{Letter} 表示任何语言中的字母。我们也可以使用 \p{L},因为 LLetter 的别名。几乎每个属性都有更短的别名。

在下面的示例中,将找到三种类型的字母:英语、格鲁吉亚语和韩语。

let str = "A ბ ㄱ";

alert( str.match(/\p{L}/gu) ); // A,ბ,ㄱ
alert( str.match(/\p{L}/g) ); // null (no matches, \p doesn't work without the flag "u")

以下是主要字符类别及其子类别

  • 字母 L
    • 小写字母 Ll
    • 修饰符 Lm,
    • 标题大小写 Lt,
    • 大写字母 Lu,
    • 其他 Lo.
  • 数字 N
    • 十进制数字 Nd,
    • 字母数字 Nl,
    • 其他 No.
  • 标点符号 P
    • 连接符 Pc,
    • 连字符 Pd,
    • 起始引号 Pi,
    • 结束引号 Pf,
    • 左括号 Ps,
    • 右括号 Pe,
    • 其他 Po.
  • 标记 M (重音等)
    • 间隔组合 Mc,
    • 封闭 Me,
    • 非间隔 Mn.
  • 符号 S
    • 货币 Sc,
    • 修饰符 Sk,
    • 数学 Sm,
    • 其他 So.
  • 分隔符 Z
    • Zl,
    • 段落 Zp,
    • 空格 Zs.
  • 其他 C
    • 控制 Cc,
    • 格式 Cf,
    • 未分配 Cn,
    • 专用使用 Co,
    • 代理 Cs.

因此,例如,如果我们需要小写字母,我们可以写 \p{Ll},标点符号:\p{P} 等等。

还有一些其他派生类别,例如

  • 字母 (Alpha),包括字母 L,加上字母数字 Nl (例如 Ⅻ - 罗马数字 12 的字符),加上一些其他符号 Other_Alphabetic (OAlpha).
  • Hex_Digit 包括十六进制数字:0-9, a-f.
  • …等等。

Unicode 支持许多不同的属性,它们的完整列表需要很多空间,因此这里有一些参考资料

示例:十六进制数字

例如,让我们查找十六进制数字,写成 xFF,其中 F 是十六进制数字 (0…9 或 A…F)。

十六进制数字可以用 \p{Hex_Digit} 表示

let regexp = /x\p{Hex_Digit}\p{Hex_Digit}/u;

alert("number: xAF".match(regexp)); // xAF

示例:汉字

让我们查找汉字。

Unicode 属性中有一个名为 Script(书写系统)的属性,它可以取值为:Cyrillic(西里尔字母)、Greek(希腊字母)、Arabic(阿拉伯字母)、Han(汉语)等等,这里有完整的列表

要查找特定书写系统中的字符,可以使用 Script=<value>,例如,要查找西里尔字母,可以使用 \p{sc=Cyrillic},要查找汉字,可以使用 \p{sc=Han},等等。

let regexp = /\p{sc=Han}/gu; // returns Chinese hieroglyphs

let str = `Hello Привет 你好 123_456`;

alert( str.match(regexp) ); // 你,好

示例:货币

表示货币的字符,例如 $¥,具有 Unicode 属性 \p{Currency_Symbol},简写别名为:\p{Sc}

让我们用它来查找“货币符号后跟数字”格式的价格。

let regexp = /\p{Sc}\d/gu;

let str = `Prices: $2, €1, ¥9`;

alert( str.match(regexp) ); // $2,€1,¥9

稍后,在文章 量词 +、*、? 和 {n} 中,我们将学习如何查找包含多个数字的数字。

总结

标志 u 启用正则表达式中的 Unicode 支持。

这意味着两件事:

  1. 4 字节的字符被正确处理:作为一个单一字符,而不是两个 2 字节字符。
  2. Unicode 属性可以在搜索中使用:\p{…}

使用 Unicode 属性,我们可以查找特定语言的单词、特殊字符(引号、货币)等等。

教程地图

评论

在评论之前请阅读…
  • 如果您有任何改进建议,请提交 GitHub 问题或拉取请求,而不是评论。
  • 如果您不理解文章中的某些内容,请详细说明。
  • 要插入几行代码,请使用 <code> 标签,要插入多行代码,请将它们用 <pre> 标签包裹,要插入超过 10 行的代码,请使用沙箱(plnkrjsbincodepen…)。