原文链接:https://javascript.info/regexp-unicode,translate with ❤️ by zhangbao.
使用 Unicode 标记 /.../u 可以启用正则表达式对查找代理对的支持。
代理对在 字符串 一章里已经说过了。
在这里我们再简要的回忆下。简单说,正常一个字符使用 2 个字节编码,这样我们能获得最多 65535 个字符,但是后来有更多的字符出现了,用 2 个字节已经不够表示了。
所以,后来的字符使用了 4 个字节编码,像 𝒳(数学里的 X)或者😄(微笑)。
下面我们来比较下不同字符的 Unicode 码:
| 字符 | Unicode | 字节 |
|---|---|---|
a |
0x0061 | 2 |
≈ |
0x2248 | 2 |
𝒳 |
0x1d4d3 | 4 |
𝒴 |
0x1d4d4 | 4 |
😄 |
0x1f604 | 4 |
像 a 和 ≈ 这样的字符占据 2 个字节,其他字符占据 4 个字节。
Unicode 是这样做的,4字节的字符表示一个整体的含义。
过去 JavaScript 并不知道这个,所以现在许多的字符串方法是有问题的。例如,length 属性会认为有两个字符:
alert( '😄'.length ); // 2alert( '𝒳'.length );// 2
但是我们看到的只有一个啊,关键是 length 属性只认 2 个字节表示的字符,因为这些字符用 4 个字节表示,所以被 length 属性认为有 2 个字符。这是不对的,他们是一个整体(称为”代理对”)。
正常情况下,正则表达式也把”长字符”当成是 2 个用 2 个字节表示的字符。
这会导致奇怪的结果,例如我们从字符串”𝒳“里查找 [𝒳𝒴]:
alert( '𝒳'.match(/[𝒳𝒴]/) ); // 奇怪的结果
结果是错误的,因为默认正则引擎并不理解代理对。它认为 [𝒳𝒴] 并不是两个字符,而是四个字符:𝒳 的左半部分(1),𝒳 的右半部分(2),𝒴 字符的左半部分(3),𝒴 字符的右半部分(4)。
所以正则引擎在字符串 𝒳 中查找的是 𝒳 的左边一半,而不是整个符号。
换句话说,搜索类似 '12'.match(/[1234]/) —— 返回了 1(𝒳 的左边一半)。
/.../u 解决了这个问题,它启用了正则表达式查找代理对的能力,所以返回的结果是正确的:
alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳
如果我们忘记这个标记的话,就会发生错误:
'𝒳'.match(/[𝒳-𝒴]/); // SyntaxError: invalid range in character class
这里的正则 [𝒳-𝒴] 可以看作 [12-34](2 是 𝒳 的右半部分,3 是 𝒴 的左半部分)。2 和 3 之间所代表的范围并不是可接受的。
当然使用标记的话就解决了这个问题:
alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴
最后,让我们注意到,如果我们无需处理代理对的话,/.../u 标记对我们没有任何作用。但在现代社会,我们经常遇到他们。
(完)
