本文为翻译
本文译者为 360 奇舞团前端开发工程师
原文标题:ECMAScript feature: regular expression pattern modifiers
原文作者:Dr. Axel Rauschmayer
原文地址:2ality.com/2025/01/reg…
传统上,我们只能将诸如 i
(用于忽略大小写)之类的正则表达式标志应用于整个正则表达式。ECMAScript特性“正则表达式模式修饰符”(由 Ron Buckton 提出)使我们能够仅将这些标志应用于正则表达式的部分内容。在这篇文章中,我们将探讨它们的工作原理以及使用场景。
正则表达式模式修饰符属性在2024年10月进入第4阶段,很可能会成为ECMAScript 2025的一部分。
什么是模式修饰符?
正则表达式的工作方式受所谓的标志影响,例如,标志 i
在匹配时忽略大小写:
> /yes/i.test('yes')
true
> /yes/i.test('YES')
true
模式修饰符使我们能够将标志应用于正则表达式的部分内容(而非整个正则表达式)。例如,在以下正则表达式中,标志 i
仅应用于“HELLO”:
> /^x(?i:HELLO)x$/.test('xHELLOx')
true
> /^x(?i:HELLO)x$/.test('xhellox')
true
> /^x(?i:HELLO)x$/.test('XhelloX')
false
从某种程度上说,模式修饰符就是内联标志。
语法
语法形式如下:
(?ims-ims:pattern)
(?ims:pattern)
(?-ims:pattern)
注意事项:
- 问号(
?
)后面的标志为启用状态。 - 连字符(
-
)后面的标志为禁用状态。 - 一个标志不能同时出现在“启用部分”和“禁用部分”。
- 不带任何标志时,此语法只是一个非捕获组:
(?:pattern)
。
让我们修改前面的示例:现在除了“HELLO”之外,整个正则表达式都不区分大小写:
> /^x(?-i:HELLO)x$/i.test('xHELLOx')
true
> /^x(?-i:HELLO)x$/i.test('XHELLOX')
true
> /^x(?-i:HELLO)x$/i.test('XhelloX')
false
支持哪些标志?
模式修饰符中可使用以下标志:
字面标志 | 属性名 | ES版本 | 描述 |
---|---|---|---|
i | ignoreCase | ES3 | 执行不区分大小写的匹配 |
m | multiline | ES3 | ^ 和 $ 匹配每行的开头和结尾 |
s | dotAll | ES2018 | 点号(. )匹配行终止符 |
欲了解更多信息,请参阅《探索JavaScript》中关于标志的部分。
其余标志不被支持,原因要么是它们会使正则表达式语义过于复杂(例如标志 v
),要么是它们只有应用于整个正则表达式才有意义(例如标志 g
)。
模式修饰符的使用场景有哪些?
使用场景:更改正则表达式部分内容的标志
有时,能够更改正则表达式部分内容的标志是很有用的。例如,Ron Buckton 解释说,更改标志 m
有助于匹配文件开头的Markdown前置元数据块(我对他的版本稍作修改):
const re = /(?-m:^)---\r?\n((?:^(?!---$).*\r?\n)*)^---$/m;
assert.equal(re.test('---a'), false);
assert.equal(re.test('---\n---'), true);
assert.equal(
re.exec('---\n---')[1],
''
);
assert.equal(
re.exec('---\na: b\n---')[1],
'a: b\n'
);
这个正则表达式是如何工作的呢?
- 默认情况下,标志
m
处于启用状态,锚点^
匹配行首,锚点 $ 匹配行尾。 - 第一个
^
有所不同:它必须匹配字符串的开头。这就是为什么我们在那里使用模式修饰符并关闭标志m
。
下面是添加了无关空白和解释性注释的正则表达式:
(?-m:^)---\r?\n # 字符串的第一行
( # 用于前置元数据的捕获组
(?: # 一行的模式(非捕获组)
^(?!---$) # 行不能以 "---" + 行结束符开头(正向先行断言)
.*\r?\n
)*
)
^---$ # 前置元数据的结束分隔符
使用场景:内联标志
在某些情况下,标志位于实际正则表达式之外会带来不便。这时模式修饰符就派上用场了。例如:
- 将正则表达式存储在配置文件中,例如以JSON格式存储。
- Regex+库提供了一种模板字面量,使创建正则表达式更加方便。指定标志的语法会增加一些杂乱度,通过模式修饰符(如果它们支持所需的标志)可以避免这种情况:
regex('i')`world`
regex`(?i:world)`
使用场景:正则表达式片段可以更改标志
在复杂应用中,如果能够由较小的正则表达式组合成大型正则表达式会很有帮助。前面提到的Regex+库支持这种做法。如果一个较小的正则表达式需要不同的标志(例如因为它想要忽略大小写),那么借助模式修饰符就可以实现。
进一步阅读
- 本文来源:ECMAScript提案“正则表达式模式修饰符”(由Ron Buckton提出)
- 《探索JavaScript》中“正则表达式(RegExp)”章节