正则表达式
正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript 中,正则表达式也是对象。这些模式与 RegExp 的 exec() 和 test() 方法一起使用,并且与 String 的 match()、matchAll()、replace()、replaceAll()、search() 和 split() 方法一起使用。本章描述 JavaScript 正则表达式。它简要概述了每个语法元素。有关每个元素的语义的详细说明,请阅读 正则表达式 参考。
创建正则表达式
你可以通过两种方式构造正则表达式
-
使用正则表达式字面量,它由斜杠之间的模式组成,如下所示
jsconst re = /ab+c/;正则表达式字面量在脚本加载时编译正则表达式。如果正则表达式保持不变,使用这种方式可以提高性能。
-
或者调用
RegExp对象的构造函数,如下所示jsconst re = new RegExp("ab+c");使用构造函数提供正则表达式的运行时编译。当你知道正则表达式模式会改变,或者你不知道模式并从其他来源(例如用户输入)获取它时,请使用构造函数。
编写正则表达式模式
正则表达式模式由简单字符(例如 /abc/)或简单字符和特殊字符的组合(例如 /ab*c/ 或 /Chapter (\d+)\.\d*/)组成。最后一个例子包含括号,它们用作记忆工具。此模式部分匹配的内容会记住以备后用,如使用分组中所述。
使用简单模式
简单模式由你想要直接匹配的字符构成。例如,模式 /abc/ 仅当精确序列 "abc" 出现时(所有字符在一起并按该顺序)才匹配字符串中的字符组合。这样的匹配将在字符串 "Hi, do you know your abc's?" 和 "The latest airplane designs evolved from slabcraft." 中成功。在这两种情况下,匹配的都是子字符串 "abc"。在字符串 "Grab crab" 中没有匹配,因为它包含子字符串 "ab c",但不包含精确的子字符串 "abc"。
使用特殊字符
当搜索匹配需要不仅仅是直接匹配时,例如查找一个或多个“b”或查找空格时,你可以在模式中包含特殊字符。例如,要匹配单个“a”后跟零个或多个“b”,再后跟“c”,你将使用模式 /ab*c/:"b" 后面的 * 表示“前一项出现 0 次或多次”。在字符串 "cbbabbbbcdebc" 中,此模式将匹配子字符串 "abbbbc"。
以下页面提供了适合每个类别的不同特殊字符列表,以及描述和示例。
- 断言指南
-
断言包括边界,表示行和单词的开头和结尾,以及以某种方式指示可能匹配的其他模式(包括先行断言、后行断言和条件表达式)。
- 字符类指南
-
区分不同类型的字符。例如,区分字母和数字。
- 分组和反向引用指南
-
分组将多个模式作为一个整体,捕获分组在使用正则表达式模式匹配字符串时提供额外的子匹配信息。反向引用引用同一正则表达式中先前捕获的分组。
- 量词指南
-
表示要匹配的字符或表达式的数量。
如果你想在一个表中查看所有可以在正则表达式中使用的特殊字符,请参见以下内容
| 字符 / 构造 | 对应文章 |
|---|---|
[xyz], [^xyz], ., \d, \D, \w, \W, \s, \S, \t, \r, \n, \v, \f, [\b], \0, \cX, \xhh, \uhhhh, \u{hhhh}, x|y |
|
^, $, \b, \B, x(?=y), x(?!y), (?<=y)x, (?<!y)x |
|
(x), (?<Name>x), (?:x), \n, \k<Name> |
|
x*, x+, x?, x{n}, x{n,}, x{n,m} |
注意: 还提供了一个更大的备忘单(仅聚合了这些单独文章的部分内容)。
转义
如果你需要字面量使用任何特殊字符(例如,实际搜索 "*"),你应该在其前面加上反斜杠进行转义。例如,要搜索 "a" 后跟 "*" 后跟 "b",你将使用 /a\*b/ —— 反斜杠“转义”了 "*",使其成为字面量而不是特殊字符。
注意:在许多情况下,当尝试匹配特殊字符时,你可以将其包裹在字符类中作为转义的替代方案,例如 /a[*]b/。
类似地,如果你正在编写正则表达式字面量并且需要匹配斜杠 (“/”),你需要转义它(否则,它会终止模式)。例如,要搜索字符串 “/example/” 后跟一个或多个字母字符,你将使用 /\/example\/[a-z]+/i—— 每个斜杠前的反斜杠使其成为字面量。
要匹配字面量反斜杠,你需要转义反斜杠。例如,要匹配字符串 "C:\",其中 "C" 可以是任何字母,你将使用 /[A-Z]:\\/ —— 第一个反斜杠转义它后面的一个,因此表达式搜索单个字面量反斜杠。
如果使用带有字符串字面量的 RegExp 构造函数,请记住反斜杠在字符串字面量中是转义字符,因此要在正则表达式中使用它,你需要在字符串字面量级别进行转义。/a\*b/ 和 new RegExp("a\\*b") 创建相同的表达式,它搜索“a”后跟字面量“*”后跟“b”。
RegExp.escape() 函数返回一个新字符串,其中正则表达式语法中的所有特殊字符都已转义。这允许你执行 new RegExp(RegExp.escape("a*b")) 来创建一个只匹配字符串 "a*b" 的正则表达式。
使用括号
正则表达式模式任何部分的括号都会导致记住该部分匹配的子字符串。一旦记住,子字符串就可以被召回以供其他使用。有关更多详细信息,请参阅分组和反向引用。
在 JavaScript 中使用正则表达式
正则表达式与 RegExp 方法 test() 和 exec() 以及 String 方法 match()、matchAll()、replace()、replaceAll()、search() 和 split() 一起使用。
| 方法 | 描述 |
|---|---|
exec() |
在字符串中执行搜索匹配。它返回一个信息数组,如果未匹配则返回 null。 |
test() |
测试字符串中是否存在匹配项。它返回 true 或 false。 |
match() |
返回一个包含所有匹配项(包括捕获分组)的数组,如果未找到匹配项则返回 null。 |
matchAll() |
返回一个包含所有匹配项(包括捕获分组)的迭代器。 |
search() |
测试字符串中是否存在匹配项。它返回匹配项的索引,如果搜索失败则返回 -1。 |
replace() |
在字符串中执行搜索匹配,并将匹配的子字符串替换为替换子字符串。 |
replaceAll() |
在字符串中执行搜索所有匹配项,并将匹配的子字符串替换为替换子字符串。 |
split() |
使用正则表达式或固定字符串将字符串拆分为子字符串数组。 |
当你需要知道字符串中是否找到模式时,使用 test() 或 search() 方法;如果需要更多信息(但执行速度较慢),请使用 exec() 或 match() 方法。如果你使用 exec() 或 match() 并且匹配成功,这些方法会返回一个数组并更新关联的正则表达式对象以及预定义的正则表达式对象 RegExp 的属性。如果匹配失败,exec() 方法返回 null(它会转换为 false)。
在以下示例中,脚本使用 exec() 方法在字符串中查找匹配项。
const myRe = /d(b+)d/g;
const myArray = myRe.exec("cdbbdbsbz");
如果你不需要访问正则表达式的属性,创建 myArray 的另一种方法是使用此脚本
const myArray = /d(b+)d/g.exec("cdbbdbsbz");
// similar to 'cdbbdbsbz'.match(/d(b+)d/g); however,
// 'cdbbdbsbz'.match(/d(b+)d/g) outputs [ "dbbd" ]
// while /d(b+)d/g.exec('cdbbdbsbz') outputs [ 'dbbd', 'bb', index: 1, input: 'cdbbdbsbz' ]
(有关不同行为的更多信息,请参阅将全局搜索标志与 exec() 配合使用。)
如果你想从字符串构造正则表达式,另一种替代方法是此脚本
const myRe = new RegExp("d(b+)d", "g");
const myArray = myRe.exec("cdbbdbsbz");
使用这些脚本,匹配成功并返回数组并更新下表中显示的属性。
| Object | 属性或索引 | 描述 | 在此示例中 |
|---|---|---|---|
myArray |
匹配的字符串和所有记住的子字符串。 | ['dbbd', 'bb', index: 1, input: 'cdbbdbsbz'] |
|
index |
匹配项在输入字符串中的 0-based 索引。 | 1 |
|
input |
原始字符串。 | 'cdbbdbsbz' |
|
[0] |
上次匹配的字符。 | 'dbbd' |
|
myRe |
lastIndex |
开始下一次匹配的索引。(此属性仅在正则表达式使用 g 选项时设置,如使用标志进行高级搜索中所述。) | 5 |
source |
模式的文本。在正则表达式创建时更新,而不是执行时更新。 | 'd(b+)d' |
如本例的第二种形式所示,你可以使用对象初始化器创建的正则表达式而不将其赋值给变量。但是,如果你这样做,每次出现都是一个新的正则表达式。因此,如果你不将其赋值给变量就使用这种形式,你就无法随后访问该正则表达式的属性。例如,假设你有以下脚本
const myRe = /d(b+)d/g;
const myArray = myRe.exec("cdbbdbsbz");
console.log(`The value of lastIndex is ${myRe.lastIndex}`);
// "The value of lastIndex is 5"
但是,如果你有以下脚本
const myArray = /d(b+)d/g.exec("cdbbdbsbz");
console.log(`The value of lastIndex is ${/d(b+)d/g.lastIndex}`);
// "The value of lastIndex is 0"
两个语句中 /d(b+)d/g 的出现是不同的正则表达式对象,因此它们的 lastIndex 属性具有不同的值。如果你需要访问使用对象初始化器创建的正则表达式的属性,你应该首先将其赋值给变量。
使用标志进行高级搜索
正则表达式具有可选标志,允许全局搜索和不区分大小写搜索等功能。这些标志可以单独使用或组合使用,并且是正则表达式的一部分。
| 标志 | 描述 | 对应属性 |
|---|---|---|
d |
为子字符串匹配生成索引。 | hasIndices |
g |
全局搜索。 | global |
i |
不区分大小写搜索。 | ignoreCase |
m |
使 ^ 和 $ 匹配每行的开头和结尾,而不是整个字符串的开头和结尾。 |
multiline |
s |
允许 . 匹配换行符。 |
dotAll |
u |
"Unicode";将模式视为 Unicode 码点序列。 | unicode |
v |
u 模式的升级版,具有更多 Unicode 功能。 |
unicodeSets |
y |
执行“粘性”搜索,从目标字符串的当前位置开始匹配。 | |
要将标志包含在正则表达式中,请使用此语法
const re = /pattern/flags;
or
const re = new RegExp("pattern", "flags");
请注意,这些标志是正则表达式不可或缺的一部分。它们无法在以后添加或删除。
例如,re = /\w+\s/g 创建一个正则表达式,它查找一个或多个字符后跟一个空格,并且它在整个字符串中查找此组合。
const re = /\w+\s/g;
const str = "fee fi fo fum";
const myArray = str.match(re);
console.log(myArray);
// ["fee ", "fi ", "fo "]
你可以替换这一行
const re = /\w+\s/g;
with
const re = new RegExp("\\w+\\s", "g");
并获得相同的结果。
m 标志用于指定多行输入字符串应被视为多行。如果使用 m 标志,^ 和 $ 匹配输入字符串中任何行的开头或结尾,而不是整个字符串的开头或结尾。
i、m 和 s 标志可以使用修饰符语法针对正则表达式的特定部分启用或禁用。
将全局搜索标志与 exec() 配合使用
带有 g 标志的 RegExp.prototype.exec() 方法会迭代地返回每个匹配项及其位置。
const str = "fee fi fo fum";
const re = /\w+\s/g;
console.log(re.exec(str)); // ["fee ", index: 0, input: "fee fi fo fum"]
console.log(re.exec(str)); // ["fi ", index: 4, input: "fee fi fo fum"]
console.log(re.exec(str)); // ["fo ", index: 7, input: "fee fi fo fum"]
console.log(re.exec(str)); // null
相比之下,String.prototype.match() 方法一次返回所有匹配项,但没有它们的位置。
console.log(str.match(re)); // ["fee ", "fi ", "fo "]
使用 Unicode 正则表达式
u 标志用于创建“Unicode”正则表达式;也就是说,支持与 Unicode 文本匹配的正则表达式。在 Unicode 模式下启用的一个重要功能是Unicode 属性转义。例如,以下正则表达式可用于匹配任意 Unicode“单词”
/\p{L}*/u;
Unicode 正则表达式的执行行为也不同。RegExp.prototype.unicode 包含对此的更多解释。
示例
使用特殊字符验证输入
在以下示例中,用户需要输入电话号码。当用户按下“检查”按钮时,脚本会检查号码的有效性。如果号码有效(匹配正则表达式指定的字符序列),脚本会显示一条消息,感谢用户并确认号码。如果号码无效,脚本会通知用户电话号码无效。
正则表达式查找
- 数据行的开头:
^ - 后跟三个数字字符
\d{3}或|一个左括号\(,后跟三个数字\d{3},后跟一个右括号\),在一个非捕获分组中(?:) - 后跟一个连字符、正斜杠或小数点,在一个捕获分组中
() - 后跟三个数字
\d{3} - 后跟第一个捕获分组中记住的匹配项
\1 - 后跟四个数字
\d{4} - 后跟数据行的结尾:
$
HTML
<p>
Enter your phone number (with area code) and then click "Check".
<br />
The expected format is like ###-###-####.
</p>
<form id="form">
<input id="phone" />
<button type="submit">Check</button>
</form>
<p id="output"></p>
JavaScript
const form = document.querySelector("#form");
const input = document.querySelector("#phone");
const output = document.querySelector("#output");
const re = /^(?:\d{3}|\(\d{3}\))([-/.])\d{3}\1\d{4}$/;
function testInfo(phoneInput) {
const ok = re.exec(phoneInput.value);
output.textContent = ok
? `Thanks, your phone number is ${ok[0]}`
: `${phoneInput.value} isn't a phone number with area code!`;
}
form.addEventListener("submit", (event) => {
event.preventDefault();
testInfo(input);
});
结果
工具
- RegExr
-
一个用于学习、构建和测试正则表达式的在线工具。
- 正则表达式测试器
-
一个在线正则表达式构建器/调试器
- 正则表达式交互式教程
-
一个在线交互式教程、备忘单和演练场。
- 正则表达式可视化器
-
一个在线可视化正则表达式测试器。