Skip to content

正则表达式是用于匹配字符串中字符组合的模式。 在 JavaScript 中,正则表达式也是对象。

这些模式被用于 RegExp 的 exec 和 test 方法, 以及 String 的 matchmatchAllreplacesearch 和 split 方法。本章介绍 JavaScript 正则表达式。

创建一个正则表达式

你可以使用以下两种方法构建一个正则表达式:

使用一个正则表达式字面量

其由包含在斜杠之间的模式组成,如下所示:

js
var re = /ab+c/;

脚本加载后,正则表达式字面量就会被编译。 当正则表达式保持==不变==时,使用此方法可获得更好的性能。

调用RegExp对象的构造函数

如下所示:

js
var re = new RegExp("ab+c");

在脚本运行过程中,用构造函数创建的正则表达式会被编译。 如果正则表达式将会 ==改变== ,或者它将会从用户输入等来源中动态地产生,就需要使用构造函数来创建正则表达式。

注意

如果将 RegExp 构造函数与字符串文字一起使用,请记住==反斜杠是字符串文字中的转义==,因此要在正则表达式中使用它,==你需要在字符串[[文字]]级别转义它==。 /a\*b/ 和new RegExp("a\*b")创建的表达式是相同的,搜索“a”后跟文字“*”后跟“b”。

在JavaScript中,解释正则表达式时需要注意字面量语法和字符串字面量语法中转义字符的处理。理解 /a\*b/new RegExp("a\*b") 创建相同的表达式的原因,我们需要了解在这两种情况下如何处理反斜杠(\)。

使用正则表达式字面量

当你使用字面量语法 /a\*b/ 来创建正则表达式时,你是直接在正则表达式内部写入模式。在这种情况下:

  • /a\*b/:这里的 \* 表示字面量中的 * 字符。反斜杠 \ 在正则表达式字面量中用来转义特殊字符(如 *),==使其失去特殊含义而被视为普通字符==。因此,这个表达式将匹配字符串 "a*b" —— 其中 a 后面直接跟一个星号 *,然后是 b

Exp 构造函数

当使用 new RegExp("a\*b") 创建正则表达式时,需要通过字符串字面量提供模式。字符串字面量和正则表达式字面量的处理方式不同:

  • "a\*b":在字符串中,每个反斜杠也必须被转义。这是因为在JavaScript的字符串字面量中,反斜杠是一个转义字符的开始。因此,为了在字符串中表示一个反斜杠,你需要使用两个反斜杠 \\ 。这里的 \* 实际上在字符串处理完毕后变成了 \*,这与直接在正则表达式字面量中写 \* 是等效的。因此,这也匹配字符串 "a*b"。
总结
  • /a\*b/ 直接在正则表达式中写 \*,意味着匹配 "a*b"。
  • new RegExp("a\*b") 在字符串中写 \*,字符串处理后变成 \*,在正则表达式中同样意味着匹配 "a*b"。

这两种方法虽然看起来书写方式不同,但实际上创建了功能完全相同的正则表达式。这种处理方式确保无论是通过正则表达式字面量还是通过字符串构造函数创建正则表达式,你都能达到预期的匹配效果。

编写一个正则表达式的模式

一个正则表达式模式是由简单的字符所构成的,比如 /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",或者寻找空格,可以在模式中使用特殊字符。比如,你可以使用 /ab*c/ 去匹配一个单独的 "a" 后面跟了零个或者多个 "b",同时后面跟着 "c" 的字符串:==*的意思是前一项出现零次或者多次==。在字符串 "cbbabbbbcdebc" 中,这个模式匹配了子字符串 "abbbbc"。

下面的页面与表格列出了一个正则表达式中可以利用的特殊字符的完整列表和描述。

1.断言(Assertions)

表示一个匹配在某些条件下发生。断言包含先行断言、后行断言和条件表达式。

断言的组成之一是边界。对于文本、词或模式,边界可以用来表明它们的起始或终止部分(如先行断言,后行断言以及条件表达式)。

类型
边界类断言
字符含义
^匹配输入的开头。如果多行模式设为 true,^ 在换行符后也能立即匹配,比如 /^A/ 匹配不了 "an A" 里面的 "A",但是可以匹配 "An A" 里面第一个 "A"。<br><br>**备注:**此字符出现在字符类开头时的含义与此不同。
$匹配输入的结束。如果多行模式设为 true,$ 在换行符前也能立即匹配,比如 /t$/ 不能匹配 "eater" 中的 "t",但是可以匹配 "eat" 中的 "t"。
\b\b — 单词边界(Word Boundary)<br><br>- \b 用来匹配一个单词的边界。单词边界是指单词字符(通常是字母、数字或下划线)与非单词字符之间的位置。<br>- 例如,在字符串 "hello world" 中,\b 可以匹配 "hello" 和 "world" 前后的位置。<br>- 正则表达式 \bhello\b 可以匹配独立的单词 "hello",但不会匹配 "hello" 在 "hello_world" 或 "hello123" 中的部分。<br><br>匹配退格字符 ([\b]), 查看 字符类
\B\B — 非单词边界(Non-Word Boundary)<br><br>- \B\b 的相反,它匹配非单词边界的位置。<br>- 这意味着 \B 匹配的是位于单词字符之间,或位于非单词字符之间的位置。<br>- 例如,正则表达式 \Bhell\B 可以匹配 "chello" 中的 "hell",但不会匹配 "hello" 中的 "hell"。
其他断言

备注: 字符也可用作量词

字符含义
x(?=y)先行断言:  ==x 被 y 跟随时匹配 x==。例如,对于/Jack(?=Sprat)/,“Jack”在跟有“Sprat”的情况下才会得到匹配./Jack(?=Sprat\\Frost)/ “Jack”后跟有“Sprat”或“Frost”的情况下才会得到匹配。不过, 匹配结果不包括“Sprat”或“Frost”。|
x(?!y)先行否定断言: ==x 没有被 y 紧随时匹配 x==。例如,对于/\d+(?!\.)/,数字后没有跟随小数点的情况下才会得到匹配。对于/\d+(?!\.)/.exec(3.141),匹配‘141’而不是‘3’。|
(?<=y)x后行断言: ==x 跟随 y 的情况下匹配 x==。例如,对于/(?&lt;=Jack)Sprat/,“Sprat”紧随“Jack”时才会得到匹配。对于/(?&lt;=Jack\\Tom)Sprat,“Sprat”在紧随“Jack”或“Tom”的情况下才会得到匹配。不过,匹配结果中不包括“Jack”或“Tom”。|
(?<!y)x后行否定断言: ==x 不跟随 y 时匹配 x==。例如,对于/(?&lt;!-)\d+/,数字不紧随 - 符号的情况下才会得到匹配。对于/(?&lt;!-)\d+/.exec(3) ,“3”得到匹配。 而/(?&lt;!-)\d+/.exec(-3)的结果无匹配,这是由于数字之前有 - 符号。|
示例
一般边界类型概述示例
js
// 使用正则表达式边界修复错误字符串
buggyMultiline = `tey, ihe light-greon apple
tangs on ihe greon traa`;

// 1) 使用 $ 修正字符串结尾处的匹配。
buggyMultiline = buggyMultiline.replace(/^t/gim, "h");
console.log(1, buggyMultiline); // 修复 'tey' =&gt; 'hey' 和 'tangs' =&gt; 'hangs',而不对 'traa' 做改动。

// 2) 使用 $ 修正字符串结尾处的匹配。
buggyMultiline = buggyMultiline.replace(/aa$/gim, "ee.");
console.log(2, buggyMultiline); // 修复 'traa' =&gt; 'tree.'。

// 3) 使用 \b 修正单词和空格边界上的字符。
buggyMultiline = buggyMultiline.replace(/\bi/gim, "t");
console.log(3, buggyMultiline); // 修复 'ihe' =&gt; 'the',而不对 'light' 做改动。

// 4) 使用 \B 匹配实体边界内的字符。
fixedMultiline = buggyMultiline.replace(/\Bo/gim, "e");
console.log(4, fixedMultiline); // 修复 'greon' =&gt; 'green',而不对 'on' 做改动。
使用 ^(控制字符)匹配输入的开头

使用 ^匹配输入的开头。在这个例子中,我们可以通过 /^A/ 正则表达式得到以 A 开头的水果。为了选择合适的水果,我们可以使用带有箭头函数的过滤方法。

js
const fruits = ["Apple", "Watermelon", "Orange", "Avocado", "Strawberry"];

// 使用正则 /^A/ 选择以'A'开头的水果。
// 这里的 '^' 只有一种含义:匹配输入的开头。

const fruitsStartsWithA = fruits.filter((fruit) =&gt; /^A/.test(fruit));
console.log(fruitsStartsWithA); // [ 'Apple', 'Avocado' ]

在第二个示例中,^ 既用于在输入开头进行匹配,也用于在字符类中使用时创建字符类补集。

js
const fruits = ["Apple", "Watermelon", "Orange", "Avocado", "Strawberry"];

// 使用正则 /^[^A]/ 选择 不是以‘A’开头的水果
// 在这个例子中,“^”控制符号表示两种含义:
// 1) 匹配输入的开头
// 2) 一个否定的字符集:[^A],意思是匹配不是‘A’的字符

const fruitsStartsWithNotA = fruits.filter((fruit) =&gt; /^[^A]/.test(fruit));

console.log(fruitsStartsWithNotA); // [ 'Watermelon', 'Orange', 'Strawberry' ]
匹配字边界
js
const fruitsWithDescription = ["Red apple", "Orange orange", "Green Avocado"];

// 选择包含以“en”或“ed”结尾的单词的描述:
const enEdSelection = fruitsWithDescription.filter((descr) =&gt;
  /(en|ed)\b/.test(descr),
);

console.log(enEdSelection); // [ 'Red apple', 'Green Avocado' ]
先行断言
js
// JS 先行断言 x(?=y) 匹配被 y 跟随的 x

const regex = /First(?= test)/g;

console.log("First test".match(regex)); // [ 'First' ]
console.log("First peach".match(regex)); // null
console.log("This is a First test in a year.".match(regex)); // [ 'First' ]
console.log("This is a First peach in a month.".match(regex)); // null
先行否定断言

例如, /\d+(?!\.)/ 匹配没有被小数点跟随且至少有一位的数字。 /\d+(?!\.)/.exec('3.141') 匹配 "141" 而不是 "3"

js
console.log(/\d+(?!\.)/g.exec("3.141")); // [ '141', index: 2, input: '3.141' ]
不同含义的“?!”:在断言和字符集中的组合用法

在像 /x(?!y)/ 这样的断言和 [^?!] 这样的字符类中,?! 组合具有不同的含义。

js
const orangeNotLemon =
  "Do you want to have an orange? Yes, I do not want to have a lemon!";

// 断言 /x(?!y)/ 和范围 /[^?!]/ 中“?!”组合用法的不同含义
const selectNotLemonRegex = /[^?!]+have(?! a lemon)[^?!]+[?!]/gi;
console.log(orangeNotLemon.match(selectNotLemonRegex)); // [ 'Do you want to have an orange?' ]

const selectNotOrangeRegex = /[^?!]+have(?! an orange)[^?!]+[?!]/gi;
console.log(orangeNotLemon.match(selectNotOrangeRegex)); // [ ' Yes, I do not want to have a lemon!' ]
后行断言
js
const oranges = ["ripe orange A", "green orange B", "ripe orange C"];

const ripeOranges = oranges.filter((fruit) =&gt; /(?&lt;=ripe )orange/.test(fruit));
console.log(ripeOranges); // [ 'ripe orange A', 'ripe orange C' ]

2.字符类

区分不同类型的字符,例如区分字母和数字。

类型
字符含义
[xyz] [a-c]一个字符类。匹配包含在方括号中的==任何字符==。你可以使用连字符指定字符范围,但如果连字符出现在方括号中的第一个或最后一个字符,则将其视为字面连字符,作为普通字符包含在字符类中。<br><br>例如,[abcd] 与 [a-d] 相同。它们匹配“brisket”中的“b”和“chop”中的“c”。<br><br>例如,[abcd-] 和 [-abcd] 匹配“brisket”中的“b”、“chop”中的“c”和“non-profit”中的“-”(连字符)。<br><br>例如,[\w-] 与 [A-Za-z0-9_-] 相同。它们都匹配“brisket”中的“b”、“chop”中的“c”和“non-profit”中的“n”。
[^xyz] [^a-c]一个==否定或补充==的字符类。也就是说,它匹配未包含在方括号中的任何字符。你可以使用连字符指定字符范围,但如果连字符出现在^后的第一个字符或方括号中的或最后一个字符,则将其视为字面连字符,作为普通字符包含在字符类中。例如,[^abc] 与 [^a-c] 相同。它们首先匹配“bacon”中的“o”和“chop”中的“h”。<br><br>备注: ^ 字符也可以表示输入的开始
.有下列含义之一:<br><br>- 匹配除行终止符之外的==任何单个字符==:\n\r\u2028 or \u2029. 例如,/.y/ 在“yes make my day”中匹配“my”和“ay”,而不是“yes”。<br>- 在字符集内,点失去了它的特殊意义,并==与文字点匹配==。<br><br>需要注意的是,m multiline 标志不会改变点的行为。因此,要跨多行匹配一个模式,可以使用字符集[^]—它将匹配任何字符,包括新行。<br><br>s“dotAll”标志允许点也匹配行终止符。
\d匹配==任何数字 (阿拉伯数字)==。相当于 [0-9]。例如,/\d/ 或 /[0-9]/ 匹配“B2is the suite number”中的“2”。
\D匹配==任何非数字 (阿拉伯数字) 的字符==。相当于[^0-9]。例如,/\D/ 或者 /[^0-9]/ 匹配“B2 is the suite number”中的“B”。
\w匹配基本拉丁字母中的==任何字母数字字符==,包括下划线。相当于 [A-Za-z0-9_]。例如,/\w/ 匹配“apple”中的“a”,匹配“$5.28”中的“5”,匹配“3D”中的“3”,以及匹配“Émanuel”中的“m”。
\W匹配任何==不是来自基本拉丁字母的单词字符==。相当于 [^A-Za-z0-9_]。例如,/\W/ 或者 /[^A-Za-z0-9_]/ 匹配“50%”中的“%”,以及匹配“Émanuel”中的“É”。
\s匹配==单个空白字符,包括空格、制表符、换页符、换行符和其他 Unicode 空格==。相当于 [\f\n\r\t\v\u0020\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。例如,\s\w*/ 匹配“foo bar”中的“bar”。
\S匹配==除空格以外的单个字符==。相当于 [^\f\n\r\t\v\u0020\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。例如,/\S\w*/ 匹配“foo bar”中的“foo”。
\t匹配==水平制表符==。
\r匹配==回车符==。
\n匹配==换行符==。
\v匹配==垂直制表符==。
\f匹配==换页符==。
[\b]匹配==退格键==。如果你正在寻找==单词边界字符==(\b),请参阅断言
\0匹配一个 ==NUL==字符。不要在此后面加上另一个数字。
\c_X_使用插入符号匹配控制字符,其中“X”是 A–Z 中的一个字母(对应于代码点 U+0001U+001F)。例如,/\cM\cJ/匹配“\r\n”。
\x_hh_匹配与代码 _hh_(两个十六进制数字)对应的字符。
\u_hhhh_匹配与值 _hhhh_(四个十六进制数字)对应的 UTF-16 代码单元。
\u_&#123;hhhh&#125;_ 或 _\u&#123;hhhhh&#125;_(仅当设置了 u 标志时。)匹配与 Unicode 值 U+_hhhh_ 或 U+_hhhhh_(十六进制数字)对应的字符。
\p&#123;_UnicodeProperty_&#125;\P&#123;_UnicodeProperty_&#125;根据字符的 Unicode 字符属性匹配字符(例如,仅匹配表情符号字符、日文_片假名_字符、中文汉字字符或日文汉字字符等)。
\指示应特殊处理或“==转义==”后面的字符。它表现为两种方式之一。<br><br>- 对于通常按字面处理的字符,表示下一个字符是特殊的,不能按字面解释。例如,/b/ 匹配字符“b”。通过在“b”前面放置反斜杠,即使用 /\b/,字符变得特殊以表示匹配单词边界。<br>- 对于通常被特殊对待的字符,表示下一个字符不是特殊的,应该按字面意思解释。例如,“*”是一个特殊字符,表示应该匹配前面的字符出现 0 次或多次;例如,/a*/ 示匹配 0 个或多个“a”。要从字面上匹配 * 需在其前面加上反斜杠;例如,/a\*/ 匹配“a*”。<br><br>备注: 要从字面上匹配此字符,请将其转义。换句话说就是搜索 \ 需要使用 /\\/
_x_|_y_析取: 匹配“x”或“y”。每个由管道符 (|) 分隔的部分称为一个 可选项 。例如,/green|red/ 匹配“green apple”中的“green”和“red apple”中的“red”。<br><br>备注: 析取是指定“一组选择”的另一种方式,但它不是字符类。析取不是原子的——你需要使用使其成为一个更大的模式的一部分。[abc] 在功能上等同于 (?:a|b|c)
示例
寻找一系列数字
js
const randomData = "015 354 8787 687351 3512 8735";
const regexpFourDigits = /\b\d&#123;4&#125;\b/g;
// \b 表示边界(即不要在单词中间开始匹配)
// \d&#123;4&#125; 表示一个数字,四次
// \b 表示另一个边界(即不要在单词中间结束匹配)

console.table(randomData.match(regexpFourDigits));
// ['8787', '3512', '8735']
寻找以 A 开头的拉丁字母单词
js
const aliceExcerpt =
  "I'm sure I'm not Ada,' she said, 'for her hair goes in such long ringlets, and mine doesn't go in ringlets at all.";
const regexpWordStartingWithA = /\b[aA]\w+/g;
// \b 表示边界(即不要在单词中间开始匹配)
// [aA] 表示字母 a 或 A
// \w+ 表示任何*拉丁字母*字符,多次

console.table(aliceExcerpt.match(regexpWordStartingWithA));
// ['Ada', 'and', 'at', 'all']
寻找一个 Unicode 字符的单词

代替拉丁字母,我们可以使用一系列 Unicode 字符来识别一个单词(从而能够处理其他语言的文本,如中文、俄语或阿拉伯语)。Unicode 的“基本多文种平面(Basic Multilingual Plane)”包含世界各地使用的大部分字符,我们可以使用字符类和范围来匹配用这些字符编写的单词。

js
const nonEnglishText = "爱丽丝 梦游 仙境";
const regexpBMPWord = /([\u0000-\u0019\u0021-\uFFFF])+/gu;
// 基本多文种平面范围是 U+0000 到 U+FFFF 但空格是 U+0020

console.table(nonEnglishText.match(regexpBMPWord));
["爱丽丝", "梦游", "仙境"];
计算元音个数
js
const aliceExcerpt =
  "There was a long silence after this, and Alice could only hear whispers now and then.";
const regexpVowels = /[AEIOUYaeiouy]/g;

console.log("元音数:", aliceExcerpt.match(regexpVowels).length);
// 元音数:26

组和反向引用(Groups and ranges)

当使用正则表达式模式与字符串匹配时,组会将多个模式组合成一个整体,捕获组会提供额外的子匹配信息。反向引用指的是同一正则表达式中以前捕获的组。

类型
字符集含义
_x_|_y_==匹配 "x" 或 "y" 任意一个字符==。例如, /green|red/ 在 "green apple" 里匹配 "green",且在 "red apple" 里匹配 "red" 。
[xyz] [a-c]字符集。 ==匹配任何一个包含的字符==。你可以使用连字符来指定字符范围,但如果连字符显示为方括号中的第一个或最后一个字符,则它将被视为作为普通字符包含在字符集中的文字连字符。也可以在字符集中包含字符类。<br><br>例如,[abcd] 是与[a-d].一样的,它们会 在"brisket" 中匹配 "b",在 "chop" 中匹配 "c" .<br><br>例如,[abcd-] 和[-abcd] 将会在 "brisket" 匹配 "b" , 在 "chop" 匹配 "c" , 并且匹配 "non-profit" 中的 "-" (连字符)<br><br>例如,[\w-] 是字符集\w和 “-”(连字符)的并集,与这种写法一样: [A-Za-z0-9_-].。他们都会 在 "brisket"中匹配“b”, 在 "chop"中匹配“c”, 在 "non-profit" 中匹配 "n"。
[^xyz] [^a-c]==一个否定的或被补充的字符集==。也就是说,它匹配任何没有包含在括号中的字符。可以通过使用连字符来指定字符范围,但是如果连字符作为方括号中的第一个或最后一个字符出现,那么它将被视为作为普通字符包含在字符集中。例如,[^abc] 和 [^a-c] 一样。它们最初匹配“bacon”中的“o”和“chop”中的“h”。<br><br>备注: ^ 字符也可以表示 输入的起始
(_x_)捕获组: ==匹配 x 并记住匹配项==。例如,/(foo)/匹配并记住“foo bar”中的“foo”<br><br>正则表达式可以有多个捕获组。结果,匹配通常在数组中捕获的组,该数组的成员与捕获组中左括号的顺序相同。这通常只是捕获组本身的顺序。当捕获组被嵌套时,这一点非常重要。使用结果元素的索引 ([1], ..., [n]) 或从预定义的 RegExp 对象的属性 ($1, ..., $9).<br><br>捕获组会带来性能损失。如果不需要收回匹配的子字符串,请选择非捕获括号 (见下面)。<br><br>String.match() 不会返回组,如果设置了 /.../g 标志。但是,你仍然可以使用 String.matchAll() to get all matches.<br><br>match() 不会返回组,如果/…但是,你仍然可以使用 String.matchAll() 来获取所有匹配项。
\_n_其中 n 是一个正整数。对正则表达式中与 n 括号匹配的最后一个子字符串的反向引用 (计算左括号)。例如,/apple(,)\sorange\1/ 匹配“apple, orange, cherry, peach”中的 "apple, orange,",其中 \1 引用了 之前使用 () 捕获的 ,
(?&lt;Name&gt;x)具名捕获组: "x"并将其存储在返回的匹配项的 groups 属性中,该属性位于&lt;Name&gt;指定的名称下。尖括号 (&lt; 和 &gt;) 用于组名。<br><br>例如,使用正则 /-(?&lt;customName&gt;\w)/ 匹配“web-doc” 中的“d”<br><br>'web-doc'.match(/-(?&lt;customName&gt;\w)/).groups //&#123;customName: "d"&#125;
(?:_x_)非捕获组: 匹配 “x”,但不记得匹配。不能从结果数组的元素中收回匹配的子字符串 ([1], ..., [n]) or from the predefined RegExp object's properties ($1, ..., $9).
示例
使用组
js
let personList = `First_Name: John, Last_Name: Doe
First_Name: Jane, Last_Name: Smith`;

let regexpNames = /First_Name: (\w+), Last_Name: (\w+)/gm;
let match = regexpNames.exec(personList);
do {
  console.log(`Hello $&#123;match[1]&#125; $&#123;match[2]&#125;`);
} while ((match = regexpNames.exec(personList)) !== null);
使用命名组
js
let users = `姓氏:李,名字:雷
姓氏:韩,名字:梅梅`;

let regexpNames = /姓氏:(?&lt;first&gt;.+),名字:(?&lt;last&gt;.+)/gm;
let match = regexpNames.exec(users);

do {
  console.log(`Hello $&#123;match.groups.first&#125; $&#123;match.groups.last&#125;`);
} while ((match = regexpNames.exec(users)) !== null);

// Hellow 李 雷
// Hellow 韩 梅梅

量词

表示匹配的字符或表达式的数量。

类型

备注: 在下文中, 不仅指单个字符,还包括字符类组和反向引用

字符集意义
x*==将前面的项“x”匹配 0 次或更多次==。例如,/bo*/ 匹配“A ghost booooed”中的“boooo”和“A bird warbled”中的“b”,但在“A goat grunt”中没有匹配。
x+==将前一项“x”匹配 1 次或更多次==。等价于 &#123;1,&#125;。例如,/a+/ 匹配“candy”中的“a”和“caaaaaaandy”中的所有“a”。
x?==将前面的项“x”匹配 0 或 1 次==。例如,/e?le?/ 匹配“angel”中的“el”和“angle”中的“le”。<br><br>==如果立即在任何 *+? 或 {} 量词之后使用,则使量词变为非贪婪匹配(匹配最小次数),而不是默认的贪婪匹配(匹配最大次数)。==
x&#123;n&#125;x&#123;n&#125; — ==确切的重复次数==<br><br>- 这个量词指定字符 x 必须恰好出现 n 次。<br>- 例如,正则表达式 a&#123;3&#125; 匹配字符串中连续出现三次的 'a',如在 "aaa"、"caaa" 或 "baaabb" 中的 "aaa"。<br>- 如果模式 a&#123;3&#125; 被用于字符串 "aaab",它只会匹配连续的三个 'a',而不会匹配两个 'a' 或四个 'a'。
x&#123;n,&#125;x&#123;n,&#125; — ==至少的重复次数==<br><br>- 这个量词指定字符 x 必须至少出现 n 次,但可以是更多次。<br>- 例如,正则表达式 a&#123;2,&#125; 匹配字符串中连续出现至少两次的 'a',如在 "aa", "aaa", "aaaa", 或 "baaa" 中的 "aa", "aaa", 和 "aaaa"。<br>- 如果模式 a&#123;2,&#125; 被用于字符串 "aaaab",它会匹配开头的四个 'a'。
x&#123;n,m&#125;- x&#123;n,m&#125; 表示字符 x 至少出现 n 次,但不超过 m 次。<br>- n 必须小于或等于 m,并且都必须是非负整数。<br><br>a&#123;2,4&#125; 会匹配字符串中 a 连续出现2次、3次或4次的情形。<br>- 在字符串 "aaa" 中,a&#123;2,4&#125; 会匹配到 "aaa"。<br>- 在字符串 "aaaaa" 中,a&#123;2,4&#125; 会匹配到前四个 'a'。<br>- 在字符串 "a" 中,则不会有任何匹配,因为 'a' 只出现了1次,不满足最小重复2次的要求。
x*? <br>x+? <br>x?? <br>x&#123;n&#125;? <br>x&#123;n,&#125;? <br>x&#123;n,m&#125;?默认情况下,像 * 和 + 这样的量词是“贪婪的”,这意味着它们试图匹配尽可能多的字符串。<br>? 作为量词修饰符时,表示懒惰(或非贪婪)匹配,它会尽可能少地匹配字符。懒惰匹配与默认的贪婪匹配相反,贪婪匹配尽可能多地匹配字符。懒惰匹配特别有用,当你只想匹配尽可能少的字符时。<br>- /&lt;.*&gt;/ 将匹配“&lt;foo&gt; &lt;bar&gt; new &lt;/bar&gt; &lt;/foo&gt;”<br>- /&lt;.*?&gt;/ 将匹配“&lt;foo&gt;
示例
重复模式
js
const wordEndingWithAs = /\w+a+\b/;
const delicateMessage = "This is Spartaaaaaaa";

console.table(delicateMessage.match(wordEndingWithAs)); // [ "Spartaaaaaaa" ]
统计单词
js
const singleLetterWord = /\b\w\b/g;
const notSoLongWord = /\b\w&#123;2,6&#125;\b/g;
const longWord = /\b\w&#123;13,&#125;\b/g;

const sentence = "Why do I have to learn multiplication table?";

console.table(sentence.match(singleLetterWord)); // ["I"]
console.table(sentence.match(notSoLongWord)); // [ "Why", "do", "have", "to", "learn", "table" ]
console.table(sentence.match(longWord)); // ["multiplication"]
可选字符
js
const britishText = "He asked his neighbour a favour.";
const americanText = "He asked his neighbor a favor.";

const regexpEnding = /\w+ou?r/g;
// \w+ 一个及以上字母
// o   跟随字母“o”,
// u?  可能跟随字母“u”
// r   跟随字母“r”

console.table(britishText.match(regexpEnding));
// ["neighbour", "favour"]

console.table(americanText.match(regexpEnding));
// ["neighbor", "favor"]
贪婪匹配与非贪婪匹配
js
const text = "I must be getting somewhere near the center of the earth.";
const greedyRegexp = /[\w ]+/;
// [\w ]      一个拉丁字母或一个空格
//      +     匹配一次及以上

console.log(text.match(greedyRegexp)[0]);
// "I must be getting somewhere near the center of the earth"
// 几乎所有文本都匹配(除了点字符)

const nonGreedyRegexp = /[\w ]+?/; // 注意问号
console.log(text.match(nonGreedyRegexp));
// "I"
// 尽可能少的匹配

正则表达式中的特殊字符(速查)

字符含义
\依照下列规则匹配:<br><br>在非特殊字符之前的反斜杠表示下一个字符是特殊字符,不能按照字面理解。例如,前面没有 "\" 的 "b" 通常匹配小写字母 "b",即字符会被作为字面理解,无论它出现在哪里。但如果前面加了 "\",它将不再匹配任何字符,而是表示一个字符边界。<br><br>在特殊字符之前的反斜杠表示下一个字符不是特殊字符,应该按照字面理解。详情请参阅下文中的 "转义(Escaping)" 部分。<br><br>如果你想将字符串传递给 RegExp 构造函数,不要忘记在字符串字面量中反斜杠是转义字符。所以为了在模式中添加一个反斜杠,你需要在字符串字面量中转义它。/[a-z]\s/i 和 new RegExp("[a-z]\\s", "i") 创建了相同的正则表达式:一个用于搜索后面紧跟着空白字符(\s 可看后文)并且在 a-z 范围内的任意字符的表达式。为了通过字符串字面量给 RegExp 构造函数创建包含反斜杠的表达式,你需要在字符串级别和正则表达式级别都对它进行转义。例如 /[a-z]:\\/i 和 new RegExp("[a-z]:\\\\","i") 会创建相同的表达式,即匹配类似 "C:" 字符串。
^匹配输入的开始。如果多行标志被设置为 true,那么也匹配换行符后紧跟的位置。<br><br>例如,/^A/ 并不会匹配 "an A" 中的 'A',但是会匹配 "An E" 中的 'A'。<br><br>当 '^' 作为第一个字符出现在一个字符集合模式时,它将会有不同的含义。反向字符集合 一节有详细介绍和示例。
$匹配输入的结束。如果多行标志被设置为 true,那么也匹配换行符前的位置。<br><br>例如,/t$/ 并不会匹配 "eater" 中的 't',但是会匹配 "eat" 中的 't'。
*匹配前一个表达式 0 次或多次。等价于 &#123;0,&#125;。<br><br>例如,/bo*/ 会匹配 "A ghost boooooed" 中的 'booooo' 和 "A bird warbled" 中的 'b',但是在 "A goat grunted" 中不会匹配任何内容。
+匹配前面一个表达式 1 次或者多次。等价于 &#123;1,&#125;。<br><br>例如,/a+/ 会匹配 "candy" 中的 'a' 和 "caaaaaaandy" 中所有的 'a',但是在 "cndy" 中不会匹配任何内容。
?匹配前面一个表达式 0 次或者 1 次。等价于 &#123;0,1&#125;。<br><br>例如,/e?le?/ 匹配 "angel" 中的 'el'、"angle" 中的 'le' 以及 "oslo' 中的 'l'。<br><br>如果紧跟在任何量词 、 +、? 或 {} 的后面,将会使量词变为非贪婪*(匹配尽量少的字符),和缺省使用的贪婪模式(匹配尽可能多的字符)正好相反。例如,对 "123abc" 使用 /\d+/ 将会匹配 "123",而使用 /\d+?/ 则只会匹配到 "1"。<br><br>还用于先行断言中,如本表的 x(?=y) 和 x(?!y) 条目所述。
.(小数点)默认匹配除换行符之外的任何单个字符。<br><br>例如,/.n/ 将会匹配 "nay, an apple is on the tree" 中的 'an' 和 'on',但是不会匹配 'nay'。<br><br>如果 s ("dotAll") 标志位被设为 true,它也会匹配换行符。
(x)像下面的例子展示的那样,它会匹配 'x' 并且记住匹配项。其中括号被称为_捕获括号_。<br><br>模式 /(foo) (bar) \1 \2/ 中的 '(foo)' 和 '(bar)' 匹配并记住字符串 "foo bar foo bar" 中前两个单词。模式中的 \1 和 \2 表示第一个和第二个被捕获括号匹配的子字符串,即 foo 和 bar,匹配了原字符串中的后两个单词。注意 \1\2、...、\n 是用在正则表达式的匹配环节,详情可以参阅后文的 \n 条目。而在正则表达式的替换环节,则要使用像 $1$2、...、$n 这样的语法,例如,'bar foo'.replace(/(...) (...)/, '$2 $1')$& 表示整个用于匹配的原字符串。
(?:x)匹配 'x' 但是不记住匹配项。这种括号叫作_非捕获括号_,使得你能够定义与正则表达式运算符一起使用的子表达式。看看这个例子 /(?:foo)&#123;1,2&#125;/。如果表达式是 /foo&#123;1,2&#125;/&#123;1,2&#125; 将只应用于 'foo' 的最后一个字符 'o'。如果使用非捕获括号,则 &#123;1,2&#125; 会应用于整个 'foo' 单词。更多信息,可以参阅下文的 使用括号的子字符串匹配 条目。
x(?=y)匹配'x'仅仅当'x'后面跟着'y'.这种叫做先行断言。<br><br>例如,/Jack(?=Sprat)/会匹配到'Jack'仅当它后面跟着'Sprat'。/Jack(?=Sprat|Frost)/匹配‘Jack’仅当它后面跟着'Sprat'或者是‘Frost’。但是‘Sprat’和‘Frost’都不是匹配结果的一部分。
(?&lt;=y)x匹配'x'仅当'x'前面是'y'.这种叫做后行断言。<br><br>例如,/(?<=Jack)Sprat/会匹配到' Sprat '仅仅当它前面是' Jack '。/(?<=Jack|Tom)Sprat/匹配‘Sprat ’仅仅当它前面是'Jack'或者是‘Tom’。但是‘Jack’和‘Tom’都不是匹配结果的一部分。
x(?!y)仅仅当'x'后面不跟着'y'时匹配'x',这被称为正向否定查找。<br><br>例如,仅仅当这个数字后面没有跟小数点的时候,/\d+(?!.)/ 匹配一个数字。正则表达式/\d+(?!.)/.exec("3.141") 匹配‘141’而不是‘3.141’
(?&lt;!_y_)_x_仅仅当'x'前面不是'y'时匹配'x',这被称为反向否定查找。<br><br>例如,仅仅当这个数字前面没有负号的时候,/(?&lt;!-)\d+/ 匹配一个数字。 <br>/(?&lt;!-)\d+/.exec('3') 匹配到 "3". <br>/(?&lt;!-)\d+/.exec('-3') 因为这个数字前有负号,所以没有匹配到。
x|y匹配‘x’或者‘y’。<br><br>例如,/green|red/匹配“green apple”中的‘green’和“red apple”中的‘red’
&#123;n&#125;n 是一个正整数,匹配了前面一个字符刚好出现了 n 次。 <br>比如, /a{2}/ 不会匹配“candy”中的'a',但是会匹配“caandy”中所有的 a,以及“caaandy”中的前两个'a'。
&#123;n,&#125;n 是一个正整数,匹配前一个字符至少出现了 n 次。<br><br>例如,/a{2,}/ 匹配 "aa", "aaaa" 和 "aaaaa" 但是不匹配 "a"。
&#123;n,m&#125;n 和 m 都是整数。匹配前面的字符至少 n 次,最多 m 次。如果 n 或者 m 的值是 0,这个值被忽略。<br><br>例如,/a{1, 3}/ 并不匹配“cndy”中的任意字符,匹配“candy”中的 a,匹配“caandy”中的前两个 a,也匹配“caaaaaaandy”中的前三个 a。注意,当匹配”caaaaaaandy“时,匹配的值是“aaa”,即使原始的字符串中有更多的 a。
[xyz]一个字符集合。匹配方括号中的任意字符,包括转义序列。你可以使用破折号(-)来指定一个字符范围。对于点(.)和星号(*)这样的特殊符号在一个字符集中没有特殊的意义。他们不必进行转义,不过转义也是起作用的。 <br>例如,[abcd] 和 [a-d] 是一样的。他们都匹配"brisket"中的‘b’,也都匹配“city”中的‘c’。/[a-z.]+/ 和/[\w.]+/与字符串“test.i.ng”匹配。
[^xyz]一个反向字符集。也就是说, 它匹配任何没有包含在方括号中的字符。你可以使用破折号(-)来指定一个字符范围。任何普通字符在这里都是起作用的。<br><br>例如,[^abc] 和 [^a-c] 是一样的。他们匹配"brisket"中的‘r’,也匹配“chop”中的‘h’。
[\b]匹配一个退格 (U+0008)。(不要和\b混淆了。)
\b匹配一个词的边界。一个词的边界就是一个词不被另外一个“字”字符跟随的位置或者前面跟其他“字”字符的位置,例如在字母和空格之间。注意,匹配中不包括匹配的字边界。换句话说,一个匹配的词的边界的内容的长度是 0。(不要和 [\b] 混淆了)<br><br>使用"moon"举例: <br>/\bm/匹配“moon”中的‘m’; <br>/oo\b/并不匹配"moon"中的'oo',因为'oo'被一个“字”字符'n'紧跟着。 <br>/oon\b/匹配"moon"中的'oon',因为'oon'是这个字符串的结束部分。这样他没有被一个“字”字符紧跟着。 <br>/\w\b\w/将不能匹配任何字符串,因为在一个单词中间的字符永远也不可能同时满足没有“字”字符跟随和有“字”字符跟随两种情况。<br><br>备注: JavaScript 的正则表达式引擎将特定的字符集定义为“字”字符。不在该集合中的任何字符都被认为是一个断词。这组字符相当有限:它只包括大写和小写的罗马字母,十进制数字和下划线字符。不幸的是,重要的字符,例如“é”或“ü”,被视为断词。
\B匹配一个非单词边界。匹配如下几种情况:<br><br>- 字符串第一个字符为非“字”字符<br>- 字符串最后一个字符为非“字”字符<br>- 两个单词字符之间<br>- 两个非单词字符之间<br>- 空字符串<br><br>例如,/\B../匹配"noonday"中的'oo', 而/y\B../匹配"possibly yesterday"中的’yes‘
\c_X_当 X 是处于 A 到 Z 之间的字符的时候,匹配字符串中的一个控制符。<br><br>例如,/\cM/ 匹配字符串中的 control-M (U+000D)。
\d匹配一个数字。``等价于 [0-9]。<br><br>例如, /\d/ 或者 /[0-9]/ 匹配"B2 is the suite number."中的'2'。
\D匹配一个非数字字符。``等价于 [^0-9]。<br><br>例如, /\D/ 或者 /[^0-9]/ 匹配"B2 is the suite number."中的'B' 。
\f匹配一个换页符 (U+000C)。
\n匹配一个换行符 (U+000A)。
\r匹配一个回车符 (U+000D)。
\s匹配一个空白字符,包括空格、制表符、换页符和换行符。等价于 [\f\n\r\t\v\u0020\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。<br><br>例如,/\s\w*/ 匹配"foo bar."中的' bar'。<br><br>经测试,\s不匹配"\u180e",在当前版本 Chrome(v80.0.3987.122) 和 Firefox(76.0.1) 控制台输入/\s/.test("\u180e") 均返回 false。
\S匹配一个非空白字符。等价于 [^\f\n\r\t\v\u0020\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。<br><br>例如,/\S\w*/ 匹配"foo bar."中的'foo'。
\t匹配一个水平制表符 (U+0009)。
\v匹配一个垂直制表符 (U+000B)。
\w匹配一个单字字符(字母、数字或者下划线)。等价于 [A-Za-z0-9_]。<br><br>例如,/\w/ 匹配 "apple," 中的 'a',"$5.28,"中的 '5' 和 "3D." 中的 '3'。
\W匹配一个非单字字符。等价于 [^A-Za-z0-9_]。<br><br>例如,/\W/ 或者 /[^A-Za-z0-9_]/ 匹配 "50%." 中的 '%'。
\_n_在正则表达式中,它返回最后的第 n 个子捕获匹配的子字符串 (捕获的数目以左括号计数)。<br><br>比如 /apple(,)\sorange\1/ 匹配"apple, orange, cherry, peach."中的'apple, orange,' 。
\0匹配 NULL(U+0000)字符,不要在这后面跟其他小数,因为 \0&lt;digits&gt; 是一个八进制转义序列。
\xhh匹配一个两位十六进制数(\x00-\xFF)表示的字符。
\uhhhh匹配一个四位十六进制数表示的 UTF-16 代码单元。
\u&#123;hhhh&#125;\u&#123;hhhhh&#125;(仅当设置了 u 标志时)匹配一个十六进制数表示的 Unicode 字符。

Escaping

如果你需要使用任何特殊字符的字面值(例如,搜索字符*),你必须通过在它前面放一个反斜杠来转义它。例如,要搜索'a'后跟*后跟'b',你应该使用/a\*b/- 反斜杠“转义”字符*,使其成为文字而非特殊符号。

类似地,如果你正在编写正则表达式文字并且需要匹配斜杠(/),那么需要转义它(否则,斜杠是正则终止符)。例如,要搜索字符串“/ example /”后跟一个或多个字母字符,你需要使用/\/example\/[a-z]+/i——每个斜杠之前使用反斜杠使它们成为普通字符。

要匹配文本符号反斜杠,你需要转义反斜杠。例如,要匹配字符串“C:\”,其中“C”可以是任何字母,你将使用/[A-Z]:\\/——第一个反斜杠转义后面的那个反斜杠,因此表达式搜索单个普通字符反斜杠。

如果将 RegExp 构造函数与字符串文字一起使用,请记住==反斜杠是字符串文字中的转义==,因此要在正则表达式中使用它,==你需要在字符串文字级别转义它==。 /a\*b/ 和new RegExp("a\\*b")创建的表达式是相同的,搜索“a”后跟文字“*”后跟“b”。

将用户输入转义为正则表达式中的一个字面字符串,可以通过简单的替换来实现:

js
function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  //$&表示整个被匹配的字符串
}

正则表达式后的"g"是一个表示全局搜索选项或标记,将在整个字符串查找并返回所有匹配结果。这将在下面的[[8.正则表达式(未仔细看)#[通过标志进行高级搜索](https //developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_expressions %E9%80%9A%E8%BF%87%E6%A0%87%E5%BF%97%E8%BF%9B%E8%A1%8C%E9%AB%98%E7%BA%A7%E6%90%9C%E7%B4%A2)|通过标志进行高级搜索]]详述。

为什么这个没有内建在 JavaScript 中?之前有计划在 RegExp 对象中添加一个 Function,但在TC39中被否决了。

使用插入语

任何正则表达式的插入语都会使这部分匹配的副字符串被记忆。一旦被记忆,这个副字符串就可以被调用于其他用途,如同 使用括号的子字符串匹配之中所述。

比如, /Chapter (\d+)\.\d*/ 解释了额外转义的和特殊的字符,并说明了这部分 pattern 应该被记忆。它精确地匹配后面跟着一个以上数字字符的字符 'Chapter ' (\d 意为任何数字字符,+ 意为 1 次以上),跟着一个小数点(在这个字符中本身也是一个特殊字符;小数点前的 \意味着这个 pattern 必须寻找字面字符 '.'),跟着任何数字字符 0 次以上。 (\d 意为数字字符, * 意为 0 次以上)。另外,插入语也用来记忆第一个匹配的数字字符。

此模式可以匹配字符串"Open Chapter 4.3, paragraph 6",并且'4'将会被记住。此模式并不能匹配"Chapter 3 and 4",因为在这个字符串中'3'的后面没有点号'.'。

括号中的"?:",这种模式匹配的子字符串将不会被记住。比如,(?:\d+) 匹配一次或多次数字字符,但是不能记住匹配的字符。

使用正则表达式

正则表达式可以被用于 RegExp 的 exec 和 test 方法 以及 String 的 matchreplacesearch 和 split 方法。 这些方法在 JavaScript 手册中有详细的解释。

方法描述
exec一个在字符串中执行查找匹配的 RegExp 方法,它返回一个数组(未匹配到则返回 null)。
test一个在字符串中测试是否匹配的 RegExp 方法,它返回 true 或 false。
match一个在字符串中执行查找匹配的 String 方法,它返回一个数组,在未匹配到时会返回 null。
matchAll一个在字符串中执行查找所有匹配的 String 方法,它返回一个迭代器(iterator)。
search一个在字符串中测试匹配的 String 方法,它返回匹配到的位置索引,或者在失败时返回 -1。
replace一个在字符串中执行查找匹配的 String 方法,并且使用替换字符串替换掉匹配到的子字符串。
split一个使用正则表达式或者一个固定字符串分隔一个字符串,并将分隔后的子字符串存储到数组中的 String 方法。

当你想要知道在一个字符串中的一个匹配是否被找到,你可以使用 test 或 search 方法;想得到更多的信息(但是比较慢)则可以使用 exec 或 match 方法。如果你使用 exec 或 match 方法并且匹配成功了,那么这些方法将返回一个数组并且更新相关的正则表达式对象的属性和预定义的正则表达式对象(详见下)。如果匹配失败,那么 exec 方法返回 null(也就是 false)。

在接下来的例子中,脚本将使用 exec 方法在一个字符串中查找一个匹配。

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

如果你不需要访问正则表达式的属性,这个脚本通过另一个方法来创建 myArray:

js
var myArray = /d(b+)d/g.exec("cdbbdbsbz");
// 和 "cdbbdbsbz".match(/d(b+)d/g); 相似。
// 但是 "cdbbdbsbz".match(/d(b+)d/g) 输出数组 [ "dbbd" ],
// 而 /d(b+)d/g.exec('cdbbdbsbz') 输出数组 [ "dbbd", "bb", index: 1, input: "cdbbdbsbz" ].

如果你想通过一个字符串构建正则表达式,那么这个脚本还有另一种方法:

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

通过这些脚本,匹配成功后将返回一个数组并且更新正则表达式的属性,如下表所示。

正则表达式执行后的返回信息

对象属性或索引描述在例子中对应的值
myArray匹配到的字符串和所有被记住的子字符串。["dbbd", "bb"]
index在输入的字符串中匹配到的以 0 开始的索引值。1
input初始字符串。"cdbbdbsbz"
[0]最近一个匹配到的字符串。"dbbd"
myRelastIndex开始下一个匹配的起始索引值。(这个属性只有在使用 g 参数时可用在 通过参数进行高级搜索 一节有详细的描述.)5
source模式字面文本。在正则表达式创建时更新,不执行。"d(b+)d"

如这个例子中的第二种形式所示,你可以使用对象初始器创建一个正则表达式实例,但不分配给变量。如果你这样做,那么,每一次使用时都会创建一个新的正则表达式实例。因此,如果你不把正则表达式实例分配给一个变量,你以后将不能访问这个正则表达式实例的属性。例如,假如你有如下脚本:

js
var myRe = /d(b+)d/g;
var myArray = myRe.exec("cdbbdbsbz");
console.log("The value of lastIndex is " + myRe.lastIndex);

这个脚本输出如下:

The value of lastIndex is 5

然而,如果你有如下脚本:

js
var 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 属性会得到不同的值。如果你需要访问一个正则表达式的属性,则需要创建一个对象初始化生成器,你应该首先把它赋值给一个变量。

使用括号的子字符串匹配

一个正则表达式模式使用括号,将导致相应的子匹配被记住。例如,/a(b)c /可以匹配字符串“abc”,并且记得“b”。回调这些括号中匹配的子串,使用数组元素 [1],……[n]

使用括号匹配的子字符串的数量是无限的。返回的数组中保存所有被发现的子匹配。下面的例子说明了如何使用括号的子字符串匹配。

下面的脚本使用 replace() 方法来转换字符串中的单词。在匹配到的替换文本中,脚本使用替代的$ 1,$ 2 表示第一个和第二个括号的子字符串匹配。

js
var re = /(\w+)\s(\w+)/;
var str = "John Smith";
var newstr = str.replace(re, "$2, $1");
console.log(newstr);

这个表达式输出 "Smith, John"。

正则表达式修饰符

正则表达式有六个可选参数 (flags) 允许全局和不分大小写搜索等。这些参数既可以单独使用也能以任意顺序一起使用,并且被包含在正则表达式实例中。

标志描述
g(global)全局搜索。
i(ignore case)不区分大小写搜索。
m(multiline)多行搜索。
s(Single line 或 DotAll)允许 . 匹配换行符。
u(Unicode)使用 unicode 码的模式进行匹配。
y(Sticky)执行“粘性 (sticky)”搜索,匹配从目标字符串的当前位置开始。

为了在正则表达式中包含标志,请使用以下语法:

js
var re = /pattern/flags;

或者

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

值得注意的是,标志是一个正则表达式的一部分,它们在接下来的时间将不能添加或删除。

例如,re = /\w+\s/g 将创建一个查找一个或多个字符后有一个空格的正则表达式,或者组合起来像此要求的字符串。

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

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

这段代码将输出 ["fee ", "fi ", "fo "]。在这个例子中,你可以将:

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

替换成:

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

并且能获取到相同的结果。

使用 .exec() 方法时,与 g 标志关联的行为是不同的。(“class”和“argument”的作用相反:在 .match() 的情况下,字符串类(或数据类型)拥有该方法,而正则表达式只是一个参数,而在 .exec() 的情况下,它是拥有该方法的正则表达式,其中字符串是参数。)对比 str.match(re)  re.exec(str)g 标志与 .exec() 方法一起使用获得迭代进展。

js
var xArray;
while ((xArray = re.exec(str))) console.log(xArray);
// produces:
// ["fee ", index: 0, input: "fee fi fo fum"]
// ["fi ", index: 4, input: "fee fi fo fum"]
// ["fo ", index: 7, input: "fee fi fo fum"]

m 标志用于指定多行输入字符串应该被视为多个行。如果使用 m 标志,^和$匹配的开始或结束输入字符串中的每一行,而不是整个字符串的开始或结束。

示例

以下例子说明了一些正则表达式的用途。

改变输入字符串的顺序

以下例子解释了正则表达式的构成和 string.split() 以及 string.replace()的用途。它会整理一个只有粗略格式的含有全名(名字首先出现)的输入字符串,这个字符串被空格、换行符和一个分号分隔。最终,它会颠倒名字顺序(姓氏首先出现)和 list 的类型。

js
// 下面这个姓名字符串包含了多个空格和制表符,
// 且在姓和名之间可能有多个空格和制表符。
var names = "Orange Trump ;Fred Barney; Helen Rigby ; Bill Abel ; Chris Hand ";

var output = ["---------- Original String\n", names + "\n"];

// 准备两个模式的正则表达式放进数组里。
// 分割该字符串放进数组里。

// 匹配模式:匹配一个分号及紧接其前后所有可能出现的连续的不可见符号。
var pattern = /\s*;\s*/;

// 把通过上述匹配模式分割的字符串放进一个叫做 nameList 的数组里面。
var nameList = names.split(pattern);

// 新建一个匹配模式:匹配一个或多个连续的不可见字符及其前后紧接着由
// 一个或多个连续的基本拉丁字母表中的字母、数字和下划线组成的字符串
// 用一对圆括号来捕获该模式中的一部分匹配结果。
// 捕获的结果稍后会用到。
pattern = /(\w+)\s+(\w+)/;

// 新建一个数组 bySurnameList 用来临时存放正在处理的名字。
var bySurnameList = [];

// 输出 nameList 的元素并且把 nameList 里的名字
// 用逗号接空格的模式把姓和名分割开来然后存放进数组 bySurnameList 中。
//
// 下面的这个替换方法把 nameList 里的元素用 $2, $1 的模式
//(第二个捕获的匹配结果紧接着一个逗号一个空格然后紧接着第一个捕获的匹配结果)替换了
// 变量 $1 和变量 $2 是上面所捕获的匹配结果。

output.push("---------- After Split by Regular Expression");

var i, len;
for (i = 0, len = nameList.length; i &lt; len; i++) {
  output.push(nameList[i]);
  bySurnameList[i] = nameList[i].replace(pattern, "$2, $1");
}

// 输出新的数组
output.push("---------- Names Reversed");
for (i = 0, len = bySurnameList.length; i &lt; len; i++) {
  output.push(bySurnameList[i]);
}

// 根据姓来排序,然后输出排序后的数组。
bySurnameList.sort();
output.push("---------- Sorted");
for (i = 0, len = bySurnameList.length; i &lt; len; i++) {
  output.push(bySurnameList[i]);
}

output.push("---------- End");

console.log(output.join("\n"));

用特殊字符检验输入

在以下例子中,我们期望用户输入一个电话号码。当用户点击“Check”按钮,我们的脚本开始检查这些数字是否合法。如果数字合法(匹配正则表达式所规定的字符序列),脚本显示一条感谢用户的信息并确认该数字。如果这串数字不合法,脚本提示用户电话号码不合法。.

包含非捕获括号 (?: 这个正则表达式寻找三个数字字符 \d&#123;3&#125; 或者 | 一个左半括号 \( 跟着三位数字 \d&#123;3&#125;, 跟着一个封闭括号 \), (结束非捕获括号 )),后跟着一个短破折号或正斜杠或小数点,随后跟随三个数字字符,当记忆字符 ([-\/\.]) 捕获并记住,后面跟着三位小数 \d&#123;3&#125;,再后面跟随记住的破折号、正斜杠或小数点 \1,最后跟着四位小数 \d&#123;4&#125;

当用户按下 Enter 设置 RegExp.input,这些变化也能被激活。

html
&lt;!doctype html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /&gt;
    &lt;meta http-equiv="Content-Script-Type" content="text/javascript" /&gt;
    &lt;script type="text/javascript"&gt;
      var re = /(?:\d&#123;3&#125;|\(\d&#123;3&#125;\))([-\/\.])\d&#123;3&#125;\1\d&#123;4&#125;/;
      function testInfo(phoneInput) {
        var OK = re.exec(phoneInput.value);
        if (!OK)
          window.alert(
            phoneInput.value + " isn't a phone number with area code!",
          );
        else window.alert("Thanks, your phone number is " + OK[0]);
      }
    &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;p&gt;
      Enter your phone number (with area code) and then click "Check". &lt;br /&gt;The
      expected format is like ###-###-####.
    &lt;/p&gt;
    &lt;form action="#"&gt;
      &lt;input id="phone" /&gt;&lt;button
        onclick="testInfo(document.getElementById('phone'));"&gt;
        Check
      &lt;/button&gt;
    &lt;/form&gt;
  &lt;/body&gt;
&lt;/html&gt;

评论区

欢迎留言、补充或勘误。

xiaoba.blog