正则表达式

正则表达式是在字符串中匹配字符组合的模式。在 JavaScript 中,正则表达式也是对象。这些模式与 exec()test() 方法一起使用 RegExp,以及与 match()matchAll()replace()replaceAll()search()split() 方法一起使用 String。本章介绍 JavaScript 正则表达式。它提供了每个语法元素的简要概述。有关每个元素语义的详细说明,请阅读 正则表达式 参考。

创建正则表达式

您可以通过两种方式之一构建正则表达式

  • 使用正则表达式字面量,它由包含在斜线之间的模式组成,如下所示
    js
    const re = /ab+c/;
    
    正则表达式字面量在脚本加载时提供正则表达式的编译。如果正则表达式保持不变,使用此方法可以提高性能。
  • 或者调用 RegExp 对象的构造函数,如下所示
    js
    const 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/ - 反斜杠“转义”"*",使其成为字面意义上的字符,而不是特殊字符。

类似地,如果您正在编写正则表达式字面量并且需要匹配斜杠(“/”),则需要对其进行转义(否则它将终止模式)。例如,要搜索字符串 "/example/" 后跟一个或多个字母字符,您将使用 /\/example\/[a-z]+/i - 每个斜杠前的反斜杠使其成为字面意义上的字符。

要匹配字面意义上的反斜杠,您需要转义反斜杠。例如,要匹配字符串 "C:\" 其中 "C" 可以是任何字母,您将使用 /[A-Z]:\\/ - 第一个反斜杠转义了第二个反斜杠,因此表达式搜索单个字面意义上的反斜杠。

如果使用带有字符串文字的 RegExp 构造函数,请记住反斜杠是字符串文字中的转义符,因此要在正则表达式中使用它,您需要在字符串文字级别对其进行转义。/a\*b/new RegExp("a\\*b") 创建相同的表达式,它搜索 "a" 后跟字面意义上的 "*" 后跟 "b"。

如果转义字符串尚未成为模式的一部分,您可以使用 String.prototype.replace() 添加它们

js
function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}

正则表达式后面的 "g" 是一个执行全局搜索的选项或标志,它在整个字符串中查找并返回所有匹配项。它将在下面 使用标志的高级搜索 中详细说明。

为什么 JavaScript 中没有内置这个功能?有一个 提案 建议在 RegExp 中添加这样一个函数。

使用括号

在正则表达式模式的任何部分使用括号会导致匹配的子字符串的那一部分被记住。一旦记住,就可以在其他地方调用该子字符串。有关更多详细信息,请参见 组和反向引用

在 JavaScript 中使用正则表达式

正则表达式与 RegExp 方法 test()exec() 以及与 String 方法 match()matchAll()replace()replaceAll()search()split() 一起使用。

方法 描述
exec() 在字符串中执行匹配搜索。它返回一个包含信息的数组或在不匹配时返回 null
test() 测试字符串中的匹配项。它返回 truefalse
match() 返回包含所有匹配项(包括捕获组)的数组,如果未找到匹配项则返回 null
matchAll() 返回包含所有匹配项(包括捕获组)的迭代器。
search() 测试字符串中的匹配项。它返回匹配项的索引,如果搜索失败则返回 -1
replace() 在字符串中执行匹配搜索,并将匹配的子字符串替换为替换子字符串。
replaceAll() 在字符串中执行所有匹配搜索,并将匹配的子字符串替换为替换子字符串。
split() 使用正则表达式或固定字符串将字符串分解为子字符串数组。

当您想知道字符串中是否找到了模式时,请使用 test()search() 方法;为了获得更多信息(但执行速度较慢),请使用 exec()match() 方法。如果您使用 exec()match() 并且匹配成功,这些方法将返回一个数组并更新关联的正则表达式对象的属性以及预定义的正则表达式对象 RegExp 的属性。如果匹配失败,exec() 方法将返回 null(强制转换为 false)。

在以下示例中,脚本使用 exec() 方法在字符串中查找匹配项。

js
const myRe = /d(b+)d/g;
const myArray = myRe.exec("cdbbdbsbz");

如果您不需要访问正则表达式的属性,创建 myArray 的另一种方法是使用此脚本

js
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() 中的全局搜索标志。)

如果您想从字符串构建正则表达式,另一种方法是使用此脚本

js
const myRe = new RegExp("d(b+)d", "g");
const myArray = myRe.exec("cdbbdbsbz");

使用这些脚本,匹配成功并返回数组并更新下表中显示的属性。

正则表达式执行结果。
Object 属性或索引 描述 在本示例中
myArray 匹配的字符串和所有记住的子字符串。 ['dbbd', 'bb', index: 1, input: 'cdbbdbsbz']
index 输入字符串中匹配项的 0 基索引。 1
input 原始字符串。 'cdbbdbsbz'
[0] 最后匹配的字符。 'dbbd'
myRe lastIndex 开始下一次匹配的索引。(此属性仅在正则表达式使用 g 选项时设置,如 使用标志进行高级搜索 中所述。) 5
source 模式文本。在创建正则表达式时更新,而不是在执行时更新。 'd(b+)d'

如本示例的第二种形式所示,您可以使用对象初始化程序创建的正则表达式,而无需将其分配给变量。但是,如果您这样做,则每次出现都是一个新的正则表达式。因此,如果您使用这种形式而无需将其分配给变量,则随后无法访问该正则表达式的属性。例如,假设您有此脚本

js
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"

但是,如果您有此脚本

js
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 执行“粘性”搜索,该搜索从目标字符串中的当前位置开始匹配。 sticky

要将标志包含在正则表达式中,请使用此语法

js
const re = /pattern/flags;

js
const re = new RegExp("pattern", "flags");

请注意,标志是正则表达式的组成部分。以后不能添加或删除它们。

例如,re = /\w+\s/g 创建一个正则表达式,该表达式查找一个或多个字符后跟一个空格,并在整个字符串中查找此组合。

js
const re = /\w+\s/g;
const str = "fee fi fo fum";
const myArray = str.match(re);
console.log(myArray);

// ["fee ", "fi ", "fo "]

您可以用

js
const re = /\w+\s/g;

替换行

js
const re = new RegExp("\\w+\\s", "g");

并获得相同的结果。

m 标志用于指定应将多行输入字符串视为多行。如果使用 m 标志,^$ 将匹配输入字符串中任何行的开头或结尾,而不是整个字符串的开头或结尾。

ims 标志可以使用 修饰符 语法为正则表达式的特定部分启用或禁用。

使用 exec() 中的全局搜索标志

RegExp.prototype.exec() 方法与 g 标志一起使用将迭代地返回每个匹配项及其位置。

js
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() 方法将一次返回所有匹配项,但不会返回它们的位置。

js
console.log(str.match(re)); // ["fee ", "fi ", "fo "]

使用 Unicode 正则表达式

u 标志用于创建“Unicode”正则表达式;也就是说,支持与 Unicode 文本匹配的正则表达式。在 Unicode 模式下启用的一个重要功能是 Unicode 属性转义。例如,以下正则表达式可用于匹配任意 Unicode“单词”

js
/\p{L}*/u;

Unicode 正则表达式的执行行为也不同。有关这方面的更多说明,请参见 RegExp.prototype.unicode

示例

注意:RegExp 页面上的许多示例也使用 Unicode 模式。

使用特殊字符验证输入

在以下示例中,用户应输入电话号码。当用户按下“检查”按钮时,脚本将检查号码的有效性。如果号码有效(与正则表达式指定的字符序列匹配),脚本将显示一条消息感谢用户并确认号码。如果号码无效,脚本将通知用户电话号码无效。

正则表达式查找

  1. 数据行的开头:^
  2. 后跟三个数字字符 \d{3}| 左括号 \(,后跟三个数字 \d{3},后跟右括号 \),位于非捕获组 (?:)
  3. 后跟一个连字符、正斜杠或小数点,位于捕获组 ()
  4. 后跟三个数字 \d{3}
  5. 后跟在(第一个)捕获组中记住的匹配项 \1
  6. 后跟四个数字 \d{4}
  7. 后跟数据行的结尾:$

HTML

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

js
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

一个在线工具,用于学习、构建和测试正则表达式。

正则表达式测试器

一个在线正则表达式构建器/调试器

正则表达式交互式教程

一个在线交互式教程、备忘单和游乐场。

正则表达式可视化工具

一个在线可视化正则表达式测试器。