ES2015 中支持 Unicode 的正则表达式
ECMAScript 2015 为正则表达式引入了两个新标志:
- y 启用“粘性”匹配。
- u 启用各种与 Unicode 相关的功能。
本文解释了 u
标志的作用。 如果之前阅读过 JavaScript 存在 Unicode 问题,它会有所帮助。
对语法的影响
在正则表达式上设置 u 标志可以在模式中使用 ES2015 Unicode 代码点转义 (\u{...}
)。
// Note: `a` is U+0061 LATIN SMALL LETTER A, a BMP symbol.
console.log(/\u{61}/u.test('a'));
// → true
// Note: `𝌆` is U+1D306 TETRAGRAM FOR CENTRE, an astral symbol.
console.log(/\u{1D306}/u.test('𝌆'));
// → true
没有标志,像 \u{1234}
这样的东西在技术上仍然可以出现在模式中,但它们不会被解释为 Unicode 代码点转义。 /\u{1234}/
等同于 /u{1234}/
,它匹配 1234 个连续的 u
符号,而不是代码点为 U+1234
的符号。
出于兼容性原因,引擎会这样做。 但是随着 u
标志的设置,这也发生了变化:像 \a
(其中 a 不是转义字符)将不再等同于 a。 因此,即使 /\a/
被视为 /a/
,/\a/u
也会抛出错误,因为 \a
不是保留的转义序列。 这使得在未来版本的 ECMAScript 中扩展正则表达式成为可能。 例如,/\p{Script=Greek}/u
根据 ES2015 抛出异常,但一旦将 Unicode 属性转义的语法添加到规范中,就可以根据 Unicode 数据库成为匹配希腊文字中所有符号的正则表达式。
对 . 操作符的影响
没有 u
标志, .
匹配除行终止符之外的任何 BMP 符号。 当设置 ES2015 u
标志时,.
也匹配星体符号。
// Note: `𝌆` is U+1D306 TETRAGRAM FOR CENTRE, an astral symbol.
const string = 'a𝌆b';
console.log(/a.b/.test(string));
// → false
console.log(/a.b/u.test(string));
// → true
const match = string.match(/a(.)b/u);
console.log(match[1]);
// → '𝌆'
对量词的影响
JavaScript 正则表达式中可用的量词有 *
、+
、?
和 {2}
、{2,}
、{2,4}
以及它们的变体。 如果没有 u
标志,如果量词跟在由星体符号组成的原子之后,它仅适用于该符号的低代理项。
// Note: `a` is a BMP symbol.
console.log(/a{2}/.test('aa'));
// → true
// Note: `𝌆` is an astral symbol.
console.log(/𝌆{2}/.test('𝌆𝌆'));
// → false
// Explanation: the previous example is equivalent to the following.
console.log(/\uD834\uDF06{2}/.test('\uD834\uDF06\uD834\uDF06'));
// → false
使用 ES2015 u
标志,量词适用于整个符号,甚至适用于星体符号。
// Note: `a` is a BMP symbol.
console.log(/a{2}/u.test('aa'));
// → true
// Note: `𝌆` is an astral symbol.
console.log(/𝌆{2}/u.test('𝌆𝌆'));
// → true
对字符类的影响
如果没有 u
标志,任何给定的字符类只能匹配 BMP 符号。 [bcd]
之类的东西按预期工作:
const regex = /^[bcd]$/;
console.log(
regex.test('a'), // false
regex.test('b'), // true
regex.test('c'), // true
regex.test('d'), // true
regex.test('e') // false
);
但是,当在字符类中使用星体符号时,JavaScript 引擎将其视为两个独立的“字符”:一个代表其代理项的一半。
// Note: `𝌆` is an astral symbol.
const regex = /^[bc𝌆]$/;
console.log(
regex.test('a'), // false
regex.test('b'), // true
regex.test('c'), // true
regex.test('𝌆') // false
);
// Explanation: the regular expression is equivalent to the following.
// const regex = /^[bc\uD834\uDF06]$/;
ES2015 u
标志允许在字符类中使用整个星体符号。
// Note: `𝌆` is an astral symbol.
const regex = /^[bc𝌆]$/u; // Or, `/^[bc\u{1D306}]$/u`.
console.log(
regex.test('a'), // false
regex.test('b'), // true
regex.test('c'), // true
regex.test('𝌆') // true
);
因此,整个星体符号也可以在字符类范围内使用,只要设置了 u
标志,一切都会按预期工作。
// Match any symbol from U+1F4A9 PILE OF POO to U+1F4AB DIZZY SYMBOL.
const regex = /[💩-💫]/u; // Or, `/[\u{1F4A9}-\u{1F4AB}]/u`.
console.log(
regex.test('💨'), // false
regex.test('💩'), // true
regex.test('💪'), // true
regex.test('💫'), // true
regex.test('💬') // false
);
u
标志还影响否定字符类。 例如,/[^a]/
等同于 /[\0-\x60\x62-\uFFFF]/
,它将匹配除 a 之外的任何 BMP 符号。 但是使用 u
标志,/[^a]/u
匹配除 a 之外的所有 Unicode 符号的更大集合。
const regex = /^[^a]$/u;
console.log(
regex.test('a'), // false
regex.test('b'), // true
regex.test('☃'), // true
regex.test('𝌆'), // true
regex.test('💩') // true
);
对字符类转义的影响
u
标志影响字符类转义 \D
、\S
和 \W
的含义。 如果没有 u
标志,\D
、\S
和 \W
匹配任何不分别与 \d
、\s
和 \w
匹配的 BMP 符号。
const regex = /^\S$/;
console.log(
regex.test(' '), // false
regex.test('a'), // true
// Note: `𝌆` is an astral symbol.
regex.test('𝌆') // false
);
使用 u
标志,\D
、\S
和 \W
也匹配星体符号。
const regex = /^\S$/u;
console.log(
regex.test(' '), // false
regex.test('a'), // true
// Note: `𝌆` is an astral symbol.
regex.test('𝌆') // true
);
它们的反向对应物 \d
、\s
和 \w
不受 u
标志的影响。 有人提议让 \d
和 \w
(以及 \b
)更支持 Unicode,但被拒绝了。
对 i 标志的影响
当同时设置 i
和 u
标志时,所有符号在比较之前立即使用 Unicode 标准提供的简单映射隐式大小写折叠。
const es5regex = /[a-z]/i;
const es6regex = /[a-z]/iu;
console.log(
es5regex.test('s'), es6regex.test('s'), // true true
es5regex.test('S'), es6regex.test('S'), // true true
// Note: U+017F canonicalizes to `S`.
es5regex.test('\u017F'), es6regex.test('\u017F'), // false true
// Note: U+212A canonicalizes to `K`.
es5regex.test('\u212A'), es6regex.test('\u212A') // false true
);
大小写折叠适用于正则表达式模式中的符号以及要匹配的字符串中的符号。
console.log(
/\u212A/iu.test('K'), // true
/\u212A/iu.test('k'), // true
/\u017F/iu.test('S'), // true
/\u017F/iu.test('s') // true
);
这种大小写折叠逻辑也适用于 \w
和 \W
字符转义,这也会影响 \b
和 B
。 /\w/iu
匹配 [0-9A-Z_a-z] 但也匹配 U+017F,因为 U+017F 规范化为匹配集中的 S。 U+212A 和 K 也是如此。
console.log(
/\w/iu.test('\u017F'), // true
/\w/iu.test('\u212A'), // true
/\W/iu.test('\u017F'), // false
/\W/iu.test('\u212A'), // false
/\W/iu.test('s'), // false
/\W/iu.test('S'), // false
/\W/iu.test('K'), // false
/\W/iu.test('k'), // false
/\b/iu.test('\u017F'), // true
/\b/iu.test('\u212A'), // true
/\b/iu.test('s'), // true
/\b/iu.test('S'), // true
/\B/iu.test('\u017F'), // false
/\B/iu.test('\u212A'), // false
/\B/iu.test('s'), // false
/\B/iu.test('S'), // false
/\B/iu.test('K'), // false
/\B/iu.test('k') // false
);
注意
:这种大小写折叠逻辑的一个烦人的结果是,根据最初的 ES2015 规范,/w/iu
不再是/\W/iu
的反函数。 还记得/\w/iu
如何匹配 [0-9A-Z_a-z] 以及 U+017F 和 U+212A 吗? 这是有道理的。 然而,在 ES2015 中,/\W/iu
也匹配 U+017F,奇怪的是,S,因为\W
包含 U+017F,它匹配 U+017F 符号本身或其规范化版本 S。同样适用于 U+212A 和 K。换句话说,/\W/iu
等同于/[^0-9a-jl-rt-zA-JL-RT-Z_]/u
。 😕 这在 2016 年 6 月得到纠正。现在,/\W/iu
不再匹配 S、K、U+017F 或 U+212A,使/\W/iu
再次成为/w/iu
的倒数。/\W/iu
现在等同于/[^0-9a-zA-Z_\u{017F}\u{212A}]/u
。
对 HTML 文档的影响
信不信由你,u
标志的存在对 HTML 文档也有影响。
input
和 textarea
元素的 pattern
属性允许我们指定一个正则表达式来验证用户的输入。 然后,浏览器会为我们提供样式和脚本钩子,让我们根据输入的有效性进行操作。
<style>
:invalid { background: red; }
:valid { background: green; }
</style>
<input pattern="a.b" value="aXXb"><!-- gets a red background -->
<input pattern="a.b" value="a𝌆b"><!-- gets a green background -->
对于通过 HTML 模式属性编译的正则表达式,始终启用 u
标志。
给开发者的建议
-
从现在开始,为我们编写的每个正则表达式使用
u
标志。 -
…但不要盲目地将
u
标志添加到现有的正则表达式中,因为它可能会以微妙的方式改变它们的含义。 -
避免组合
u
和i
标志。 最好是显式地将所有字母大小写包含在正则表达式本身中,而不是对隐式大小写折叠感到惊讶。 - 使用转译器确保我们的代码可以在任何地方运行,包括遗留环境。
相关文章
在 Python 中将 Unicode 字符转换为 ASCII 字符串
发布时间:2023/03/24 浏览次数:180 分类:Python
-
它演示了如何在 Python 中将 Unicode 字符转换为字符串
Python 中的原始字符串和 Unicode 字符串
发布时间:2023/03/01 浏览次数:156 分类:Python
-
区分 'u' 和 'r' 字符串标志并了解原始字符串文字在 python 中的工作原理。
Python2 和 3 中如何将(Unicode)字符串转换为小写
发布时间:2023/03/01 浏览次数:97 分类:Python
-
本文演示了在 Python2 和 3 中如何将(unicode)字符串转换为小写。
Python 中 UnicodeDecodeError: 'charmap' codec can't decode byte 错误
发布时间:2023/02/17 浏览次数:99 分类:Python
-
Python UnicodeDecodeError: charmap codec cant decode byte in position 发生在我们指定不正确的编码或在打开文件时未显式设置编码关键字参数时。 要解决错误,请指定正确的编码,例如 utf-8 。 下面是
ES2015 const 与不变性无关
发布时间:2023/01/09 浏览次数:112 分类:WEB前端
-
这似乎是一个非常普遍的误解。 我经常在博客文章遇到它。 这是我试图把事情弄清楚的尝试。 const 创建一个不可变的绑定 ES2015 const 并不表示一个值是常量或不可变的。 const 值绝对可
ES2015 中有效的 JavaScript 变量名
发布时间:2023/01/09 浏览次数:68 分类:学无止境
-
ES2015 更新了标识符的语法。 这会影响很多事情,但最重要的是,标识符可以用作变量名称,并且标识符名称是有效的不带引号的属性名称。 这篇文章描述了与旧 ES5 行为相比的可观察
JavaScript 正则表达式中的 Unicode 属性转义
发布时间:2023/01/08 浏览次数:173 分类:JavaScript
-
ES2018 为 JavaScript 正则表达式 添加了对形式为 \p{...} 和 \P{...} 的 Unicode 属性转义的支持。 本文解释了 Unicode 属性转义是什么、它们如何工作以及它们为何有用。 介绍 Unicode 标准为每个符
JavaScript 存在 Unicode 问题
发布时间:2023/01/08 浏览次数:146 分类:JavaScript
-
JavaScript 处理 Unicode 的方式至少可以说是令人惊讶的。 这篇文章解释了 JavaScript 中与 Unicode 相关的痛点,提供了常见问题的解决方案,并解释了 ECMAScript 6 标准如何改善这种情况。 Uni