39. 正则表达式(RegExp)
原文: http://exploringjs.com/impatient-js/ch_regular-expressions.html
除非另有说明,否则 ES5 及更高版本支持所有正则表达式功能。
39.1. 创建正则表达式
39.1.1. 字面义式 vs. 构造函数式
创建正则表达式的两种主要方法是:
- 字面义式:
/abc/ui
,被静态编译(在加载时)。 - 构造函数式:
new RegExp('abc', 'ui')
,被动态编译(在运行时)- 第二个参数是可选的。
两个正则表达式都有相同的两个部分:
- 表达式体
abc
- 实际正则表达式。 - 标志
u
和i
。标志配置了表达式模式如何被解释:u
切换到 _Unicode 模式 _ 。i
启用大小写不敏感匹配。
39.1.2. 克隆和非破坏性地修改正则表达式
构造函数RegExp()
有两种变体:
new RegExp(pattern : string, flags = '')
通过指定的
pattern
创建新的正则表达式。如果缺少flags
,则使用空字符串''
。new RegExp(regExp : RegExp, flags = regExp.flags)
[ES6]regExp
被克隆。如果提供了flags
,则它确定副本的标志。
第二个变体可用于克隆正则表达式,在修改它们时也是可选的。标志是不可变的,这是改变它们的唯一方法。例如:
function copyAndAddFlags(regExp, flags='') {
// The constructor doesn’t allow duplicate flags,
// make sure there aren’t any:
const newFlags = [...new Set(regExp.flags + flags)].join('');
return new RegExp(regExp, newFlags);
}
assert.equal(/abc/i.flags, 'i');
assert.equal(copyAndAddFlags(/abc/i, 'g').flags, 'gi');
39.2. 语法
39.2.1. 语法字符
在正则表达式的顶层,以下 语法字符 是特殊的。它们通过前缀反斜杠(\
)进行转义。
\ ^ $ . * + ? ( ) [ ] { } |
在正则表达式字面义中,您还必须转义斜杠(用new RegExp()
就没有必要了):
> /\//.test('/')
true
> new RegExp('/').test('/')
true
39.2.2. 基本原子
原子 是正则表达式的基本构建块。
- 模式字符:除语法字符(
^
,$
等)外的所有字符。模式字符匹配自己。示例:A b %
.
匹配任何字符。您可以使用标志/s
(dotall
)来控制点.
是否与行终止符匹配(下面详述)。- 字符转义(每个转义匹配一个固定字符):
- 控制转义(用于一些控制字符):
\f
:换页(FF)\n
:换行(LF)\r
:回车(CR)\t
:角色列表\v
:行列表
- 任意控制字符:
\cA
(Ctrl-A),\cB
(Ctrl-B)等。 - Unicode 代码单位:
\u00E4
- Unicode 代码点(需要标志
/u
):\u{1F44D}
- 控制转义(用于一些控制字符):
- 字符类转义(每个转义匹配一组字符中的一个):
\d
:数字(与[0-9]
相同)\D
:非数字
\w
:“单词”字符(与[A-Za-z0-9_]
相同)\W
:非单词字符
\s
:空白(空格,制表符,行终止符等)\S
:非空白
- Unicode 属性转义(ES2018):
\p{White_Space}
,\P{White_Space}
等.- 需要标志
/u
。 - 在下一小节中描述。
- 需要标志
39.2.2.1. Unicode 属性转义
Unicode 属性转义看起来像这样:
\p{prop=value}
:匹配属性prop
具有值value
的所有字符。\P{prop=value}
:匹配所有没有属性prop
的字符,其值为value
。\p{bin_prop}
:匹配二进制属性bin_prop
为 True 的所有字符。\P{bin_prop}
:匹配二进制属性bin_prop
为 False 的所有字符。
评论:
如果设置了标志
/u
,则只能使用 Unicode 属性转义。没有/u
,\p
与p
相同。如果属性是
General_Category
,则表格(3)和(4)可以用作缩写。例如,\p{Lowercase_Letter}
是\p{General_Category=Lowercase_Letter}
的缩写
例子:
检查空格:
> /^\p{White_Space}+$/u.test('\t \n\r') true
检查希腊字母:
> /^\p{Script=Greek}+$/u.test('μετά') true
删除任何字母:
> '1π2ü3é4'.replace(/\p{Letter}/ug, '') '1234'
删除小写字母:
> 'AbCdEf'.replace(/\p{Lowercase_Letter}/ug, '') 'ACE'
进一步阅读:
- Unicode 属性及其值的列表:“Unicode 标准附件#44:Unicode 字符数据库”(编辑:Mark Davis,LaurenţiuIancu,Ken Whistler)
- Unicode 属性更深入地转义:“探索 ES2018 和 ES2019”中的章节“RegEx Unicode 属性转义”
39.2.3。角色类
匹配一组字符中的一个:
[abc]
- 匹配不在集合中的任何字符:
[^abc]
- 匹配不在集合中的任何字符:
在方括号内,只有以下字符是特殊的,必须进行转义:
^ \ - ]
^
只有先到时才需要进行转义。-
如果是第一个或最后一个,则无需转义字符转义(
\n
,\u{1F44D}
)和字符类转义(\d
,\p{White_Space}
)照常工作。- 例外:在方括号内,
\b
匹配退格。在其他地方,它匹配单词边界。
- 例外:在方括号内,
字符范围通过短划线指定:
[a-z]
,[^a-z]
39.2.4. 组
- 位置捕获组:
(#+)
- 反向引用:
\1
,\2
等
- 反向引用:
- 命名捕获组(ES2018):
(?<hashes>#+)
- 反向引用:
\k<hashes>
- 反向引用:
- 非捕获组:
(?:#+)
39.2.5. 量词
默认情况下,以下所有量词都是贪心的:
?
:匹配从不或一次*
:匹配零次或多次+
:匹配一次或多次{n}
:匹配n
次{n,}
:匹配n
次或更多次{n,m}
:至少匹配n
次,最多m
次。
为了使他们不情愿,在他们后面加上问号(?
):
> /".*"/.exec('"abc"def"')[0] // greedy
'"abc"def"'
> /".*?"/.exec('"abc"def"')[0] // reluctant
'"abc"'
39.2.6. 断言
^
仅在输入的开头匹配$
仅在输入结束时匹配\b
仅匹配单词边界\B
仅在不在单词边界时匹配
向前看:
- 如果
pattern
匹配下一个(积极前瞻),则(?=«pattern»)
匹配。示例(“X
后跟小写字母的序列” - 请注意X
本身不是匹配的子字符串的一部分):
> 'abcX def'.match(/[a-z]+(?=X)/g) [ 'abc' ]
- 如果
pattern
与接下来的内容不匹配,则(?!«pattern»)
匹配(消极前瞻)。示例(“小写字母的序列,后面没有X
”)
> 'abcX def'.match(/[a-z]+(?!X)/g) [ 'ab', 'def' ]
- 进一步阅读:“探索 ES2018 和 ES2019”中的“RegExp lookbehind 断言”(也涵盖了先行断言)
- 如果
向后看 (ES2018):
- 如果
pattern
与之前的相符,则(?<=«pattern»)
匹配(积极后观)
> 'Xabc def'.match(/(?<=X)[a-z]+/g) [ 'abc' ]
- 如果
pattern
与之前的不匹配,则(?<!«pattern»)
匹配(消极后观)
> 'Xabc def'.match(/(?<!X)[a-z]+/g) [ 'bc', 'def' ]
- 如果
39.2.7. 分离(|
)
警告:此运算符的优先级较低。必要时使用组:
^aa|zz$
匹配以aa
开头和/或以zz
结束的所有字符串。请注意,|
的优先级低于^
和$
。^(aa|zz)$
匹配两个字符串'aa'
和'zz'
。^a(a|z)z$
匹配两个字符串'aaz'
和'azz'
。
39.3. 标志
表格 20: 这些是JavaScript支持的正则表达式标志。
字面义标志 | 属性名称 | ES | 描述 |
---|---|---|---|
g | global | ES3 | 匹配多次 |
i | ignoreCase | ES3 | 不区分大小写 |
m | multiline | ES3 | 每行^ 和$ 匹配 |
s | dotall | ES2018 | 点. 匹配行终止符 |
u | unicode | ES6 | Unicode 模式(推荐) |
y | sticky | ES6 | 匹配之间没有字符 |
JavaScript 中提供了以下正则表达式标志(表格20 提供了紧凑的概述):
/g
(.global
):从根本上改变方法RegExp.prototype.test()
,RegExp.prototype.exec()
和String.prototype.match()
的工作方式。将与这些方法一起详细解释。简而言之:如果没有/g
,方法只考虑输入字符串中正则表达式的第一个匹配项。使用/g
,他们会考虑所有匹配。/i
(.ignoreCase
):打开不区分大小写的匹配:
> /a/.test('A')
false
> /a/i.test('A')
true
/m
(.multiline
):如果该标志打开,^
匹配每一行的开头,$
匹配每一行的结尾。如果它关闭,^
匹配整个输入字符串的开头,$
匹配整个输入字符串的结尾。
> 'a1\na2\na3'.match(/^a./gm)
[ 'a1', 'a2', 'a3' ]
> 'a1\na2\na3'.match(/^a./g)
[ 'a1' ]
/u
(.unicode
):该标志用于打开正则表达式的 Unicode 模式。该模式将在下一小节中解释。/y
(.sticky
):该标志仅与/g
一起使用。当两者都打开时,第一个之后的任何匹配必须直接跟随前一个匹配(它们之间没有任何字符)。
> 'a1a2 a3'.match(/a./gy)
[ 'a1', 'a2' ]
> 'a1a2 a3'.match(/a./g)
[ 'a1', 'a2', 'a3' ]
/s
(.dotall
):默认情况下,点与行终止符不匹配。有了这个标志,它确实:
> /./.test('\n')
false
> /./s.test('\n')
true
旧版 ECMAScript 版本的替代方案:
> /[^]/.test('\n')
true
39.3.1. 标志:通过/u
的 Unicode 模式
标志/u
为正则表达式打开特殊的 Unicode 模式。该模式支持多种功能:
在模式中,您可以使用 Unicode 代码点转义(例如
\u{1F42A}
)来指定字符。诸如\u03B1
之类的代码单元转义只有四个十六进制数字的范围(等于基本的多语言平面)。在模式中,您可以使用 Unicode 属性转义(ES2018),例如
\p{White_Space}
。现在禁止许多转义(这使得之前的功能成为可能):
> /\a/
/\a/
> /\a/u
SyntaxError: Invalid regular expression: /\a/: Invalid escape
> /\-/
/\-/
> /\-/u
SyntaxError: Invalid regular expression: /\-/: Invalid escape
> /\:/
/\:/
> /\:/u
SyntaxError: Invalid regular expression: /\:/: Invalid escape
- 匹配的原子单位(“字符”)是代码点,而不是代码单元。
以下小节将更详细地解释最后一项。它们使用以下 Unicode 字符来解释原子单位何时是代码点以及何时是代码单元:
const codePoint = '