JavaScript之RegExp与正则

本文最后更新于 2025年8月21日 下午

参考:

正则的核心

正则就是匹配模式,要么匹配位置,要么匹配字符。划重点,牢记这个核心。

下图是一个字符串:I Love U,箭头表示要匹配的位置,框框表示要匹配的字符(包括空格)。

正则表达式

匹配位置

模式 说明
^ 匹配开头的位置,当正则有修饰符 m 时(多行文本),表示匹配行开头位置
$ 匹配结尾的位置,当正则有修饰符 m 时(多行文本),表示匹配行结尾位置
\b 匹配单词边界,即匹配上面示例中的 I、Love、U 前后的位置
\B 匹配非单词边界,与 \b 相反,即匹配上面示例中的 o、v、e 前后的位置
(?=表达式) 正向先行断言,指在某个位置的右侧必须能匹配表达式
(?!表达式) 反向先行断言,指在某个位置的右侧不能匹配表达式
(?<=表达式) 正向后行断言,指在某个位置的左侧必须能匹配表达式
(?<!表达式) 反向后行断言,指在某个位置的左侧不能匹配表达式

匹配以 ‘javascript’ 开头的字符串
匹配以 ‘javascript’ 结尾的字符串

1
2
/^javascript/.test("javascript is my favorite"); // true
/javascript$/.test("this code in javascript"); // true

匹配有边界的 ‘code’ 单词

1
2
3
/\bcode\b/.test("bar code"); // true
/\bcode\b/.test("barcode"); // false
/code\b/.test("barcode "); // true

匹配姓’李’的名字

1
2
3
4
5
/^李.+/.test("李逍遥"); // true
/^李.+/.test("李"); // false
/^李.+/.test("慕容李逍遥"); // false
// "李" 左侧可以为空格不能为非空格字符
/(?<=[\s]?)(?<!\S)李.+/.test("李逍遥"); // true

匹配字符

字符

模式 说明
字母、数字 匹配字符本身
\0 匹配 NUL 字符
\t 匹配水平制表符
\v 匹配垂直制表符
\n 匹配换行符
\r 匹配回车符
\f 匹配换页符
\xnn 匹配拉丁字符
\uxxxx 匹配 Unicode 字符
\cX 匹配 ctrl+X
[\b] 匹配 Backspace 键

字符组

模式 说明
[abc] 匹配 “a”、”b”、”c” 其中任何一个字符
[a-d1-4] 匹配 “a”、”b”、”c”、”d”、”1”、”2”、”3”、”4” 其中任何一个字符
[^abc] 匹配除了 “a”、”b”、”c” 之外的任何一个字符
[^a-d1-4] 匹配除了 “a”、”b”、”c”、”d”、”1”、”2”、”3”、”4” 之外的任何一个字符
. 通配符,匹配除了少数字符(\n)之外的任意字符
\d 匹配数字,等价于 [0-9]
\D 匹配非数字,等价于 [^0-9]
\w 匹配单词字符,等价于 [a-zA-Z0-9_]
\W 匹配非单词字符,等价于 [^a-zA-Z0-9_]
\s 匹配空白符,等价于 [ \t\v\n\r\f]
\S 匹配非空白符,等价于 [^ \t\v\n\r\f]

[...] 字符组语法类似 Javascript 中的数组,简单的理解就是正则表达式会匹配 [...] 中的某个字符/表达式,相当于将字符组内的字符遍历匹配。

匹配 ‘Javascript’ 和 ‘javascript’

1
2
3
4
5
6
7
const regex = /[jJ]avascript/;
regex.test("javascript"); // true
regex.test("Javascript"); // true

const regex1 = /[jJ]ava/;
regex1.test("javascript"); // true
regex1.test("Javascript"); // true

匹配 ‘我爱你’ 或 ‘我想你’ 或 ‘我’ + 数字 + ‘你’

1
2
3
4
5
const regex = /我[\d爱想]你/;
regex.test("我爱你"); // true
regex.test("我想你"); // true
regex.test("520, 我2你"); // true
regex.test("我打你"); // false

匹配’爱’后面不包含’你’

1
2
3
const regex = /爱[^你]/;
regex.test("我爱你"); // false
regex.test("我爱他"); // true

量词

贪婪模式――在匹配成功的前提下,尽可能多的去匹配。
惰性模式――在匹配成功的前提下,尽可能少的去匹配。

模式 说明 模式 说明
{n,m} 连续出现 n 到 m 次(贪婪模式) {n,m}? 连续出现 n 到 m 次(惰性模式)
{n,} 至少连续出现 n 次(贪婪模式) {n,}? 至少连续出现 n 次(惰性模式)
{n} 连续出现 n 次(贪婪模式) {n}? 连续出现 n 次(惰性模式)
? 等价于 {0,1}(贪婪模式) ?? 等价于 {0,1}?(惰性模式)
+ 等价于 {1,}(贪婪模式) +? 等价于 {1,}?(惰性模式)
* 等价于 {0,}(贪婪模式) *? 等价于 {0,}?(惰性模式)

匹配手机号码,假设手机号码规则如下:

  • 必须是 11 位的数字
  • 第一位数字必须以 1 开头
  • 第二位数字可以是 [3,4,5,7,8] 中的任意一个
  • 后面 9 个数是 [0-9] 中的任意一个数字
1
2
3
4
const regex = /^1[34578]\d{9}/;
regex.test("18711001111"); // true
regex.test("13712345678"); // true
regex.test("12345678911"); // false

括号

括号主要是用来分组。

模式 说明
(ab) 捕获型分组:把 “ab” 当成一个整体,表示 “ab” 至少连续出现一次
(?:ab) 非捕获型分组:与 (ab) 的区别是不捕获数据
(good | nice) 捕获型分支结构:匹配 “good” 或 “nice”
(?:good | nice) 非捕获型分支结构:与 (good | nice) 的区别是不捕获数据
\num 反向引用:比如 \2,表示引用的是第二个括号里的捕获的数据

视频文件的后缀名有 .mp4.avi.wmv.rmvb 用正则表达式提取所有的视频文件的后缀

1
2
3
4
5
6
7
8
const regex = /.+(\.mp4|\.avi|.wmv|\.rmvb)/;

regex.exec("海贼王.avi");
// ['海贼王.avi', '.avi', index: 0, input: '海贼王.avi', groups: undefined]
regex.exec("资料.rmvb");
// ['资料.rmvb', '.rmvb', index: 0, input: '资料.rmvb', groups: undefined]
regex.test("朋友.mp3");
// null

JS API

Javascript 中可以通过以下两种方式写正则:

  • 正则表达式字面量
  • 通过构造函数 RegExp 的实例

创建一个正则用于精确匹配字符串 ‘test’:

1
2
3
let regExp = /test/;
let regExp = new RegExp("test");
let regExp = new RegExp(/test/);

修饰符

模式 说明
g 全局匹配,找到所有满足匹配的子串,而不是默认只匹配首次结果(global)
i 匹配过程中,忽略英文字母大小写(ignore case)
m 多行匹配,把 ^ 和 $ 变成行开头和行结尾(multiline)
u 匹配过程中,允许使用 Unicode 点转义符(unicode)
y 开启粘连匹配,正则表达式执行粘连匹配时试图从最后一个匹配位置开始(sticky)

实例属性

1
2
3
4
5
6
7
8
RegExp.prototype.flags;
RegExp.prototype.dotAll;
RegExp.prototype.global;
RegExp.prototype.ignoreCase;
RegExp.prototype.multiline;
RegExp.prototype.source;
RegExp.prototype.sticky;
RegExp.prototype.unicode;

flags 标志

flags 属性返回一个字符串,由当前正则表达式对象的标志组成。

标志以字典序排序,从左到右,即”gimuy”。

1
2
/foo/gi.flags; // "gi"
/bar/muy.flags; // "muy"

dotAll 标志

dotAll 属性表明是否在正则表达式中一起使用”s”修饰符(引入/s 修饰符,使得可以匹配任意单个字符)。

dotAll 是一个只读的属性,属于单个正则表达式实例。

global 全局匹配

global 属性表明正则表达式是否使用了 “g” 标志。

global 的值是布尔对象,是一个正则表达式实例的只读属性。

“g” 标志意味着正则表达式应该测试字符串中所有可能的匹配。

1
2
3
const regex = new RegExp("foo", "g");
regex.global; // true
/fool/g.global; // true

ignoreCase 忽略大小写

ignoreCase 属性表明正则表达式是否使用了 “i” 标志。

ignoreCase 的值是布尔对象,是一个正则表达式实例的只读属性。

“i” 标志意味着在字符串进行匹配时,应该忽略大小写。

1
2
3
const regex = new RegExp("foo", "i");
regex.ignoreCase; // true
/fool/i.ignoreCase; // true

multiline 多行搜索

multiline 属性表明正则表达式是否使用了 “m” 标志。

multiline 的值是布尔对象,是一个正则表达式实例的只读属性。

“m” 标志意味着一个多行输入字符串被看作多行。

1
2
3
const regex = new RegExp("foo", "m");
regex.multiline; // true
/fool/m.multiline; // true

source 正则文本

source 属性返回一个值为当前正则表达式对象的模式文本的字符串,该字符串不会包含正则字面量两边的斜杠以及任何的标志字符。

1
2
const regex = /fooBar/gi;
regex.source; // "fooBar",不包含 /.../ 和 "ig"。

sticky 粘连匹配

sticky 属性表明正则表达式是否使用了 “y” 标志。

sticky 的值是布尔对象,是一个正则表达式实例的只读属性。

“y” 标志意味着仅从正则表达式的 lastIndex 属性表示的索引处为目标字符串匹配(并且不会尝试从后续索引匹配)。如果一个表达式同时指定了 sticky 和 global,其将会忽略 global 标志。

1
2
3
4
5
6
7
const str = "#foo#";
const regex = /foo/y;
regex.lastIndex = 1;
regex.test(str); // true
regex.lastIndex = 5;
regex.test(str); // false(lastIndex 被 sticky 标志考虑到,从而导致匹配失败)
regex.lastIndex; // 0(匹配失败后重置)

unicode 点转义符

unicode 属性表明正则表达式是否使用了 “u” 标志。

unicode 的值是布尔对象,是一个正则表达式实例的只读属性。

“u” 标志开启了多种 Unicode 相关的特性。使用 “u” 标志,任何 Unicode 代码点的转义都会被解释。

1
2
const regex = new RegExp("\u{61}", "u");
regex.unicode; // true

实例方法

1
2
RegExp.prototype.exec();
RegExp.prototype.test();

exec() 搜索匹配

exec() 方法在一个指定字符串中执行一个搜索匹配。返回一个结果数组或 null。

接收一个参数:

  • str(必需):要匹配正则表达式的字符串。

如果匹配失败,exec() 方法返回 null,并将正则表达式的 lastIndex 重置为 0。
如果匹配成功,exec() 方法返回一个数组,并更新正则表达式对象的 lastIndex 属性。完全匹配成功的文本将作为返回数组的第一项,从第二项起,后续每项都对应一个匹配的捕获组。数组还具有以下额外的属性:

  • index:匹配到的字符位于原始字符串的基于 0 的索引值。
  • input:匹配的原始字符串。
  • groups:一个命名捕获组对象,其键是名称,值是捕获组。若没有定义命名捕获组,则 groups 的值为 undefined。
  • indices (可选):此属性仅在设置了 d 标志位时存在。它是一个数组,其中每一个元素表示一个子字符串的边界。每个子字符串匹配本身就是一个数组,其中第一个元素表示起始索引,第二个元素表示结束索引。

当正则表达式设置 g 标志位时,可以多次执行 exec() 方法来查找同一个字符串中的成功匹配。当这样做时,查找将从正则表达式的 lastIndex 属性指定的位置开始。(test() 也会更新 lastIndex 属性)。注意,即使再次查找的字符串不是原查找字符串时,lastIndex 也不会被重置,它依旧会从记录的 lastIndex 开始。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const string = "table football, football";
const regex1 = RegExp("foo*");
const regex2 = RegExp("foo*", "g");
let array;
regex1.lastIndex; // 0
regex1.exec(string);
// ['foo', index: 6, input: 'table football, football', groups: undefined]
regex1.lastIndex; // 0
regex1.exec(string);
// ['foo', index: 6, input: 'table football, football', groups: undefined]
regex2.lastIndex; // 0
regex2.exec(string);
// ['foo', index: 6, input: 'table football, football', groups: undefined]
regex2.lastIndex; // 9
regex2.exec(string);
// ['foo', index: 16, input: 'table football, football', groups: undefined]
regex2.lastIndex; // 19
regex2.exec(string);
// null
regex2.lastIndex; // 0
regex2.exec(string);
// ['foo', index: 6, input: 'table football, football', groups: undefined]

const re = /quick\s(?<color>brown).+?(jumps)/dgi;
re.exec("The Quick Brown Fox Jumps Over The Lazy Dog");
//  [
// 'Quick Brown Fox Jumps',
// 'Brown',
// 'Jumps',
// index: 4,
// input: 'The Quick Brown Fox Jumps Over The Lazy Dog',
// groups: { color: "brown" },
// indices: [[4, 25], [10, 15], [20, 25], groups: { color: [10, 15] }]
// ]

test() 是否匹配

test() 方法执行一个检索,用来查看正则表达式与指定的字符串是否匹配。返回 truefalse

接收一个参数:

  • str(必需):要匹配正则表达式的字符串。

想要知道一个正则表达式是否与指定的字符串匹配时,可以使用 test()(类似 String.prototype.search() ),差别在于 test() 返回一个布尔值,而 search() 返回索引(如果找到)或者 -1(如果没找到)。

想要知道更多信息(然而执行比较慢),可使用 exec() 方法(类似 String.prototype.match() )。和 exec() (或者组合使用)一样,在相同的全局正则表达式实例上多次调用 test() 将会越过之前的匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
const string = "table football";
const regex1 = new RegExp("foo*");
const regex2 = new RegExp("foo*", "g");
regex1.lastIndex; // 0
regex1.test(string); // true
regex1.lastIndex; // 0
regex1.test(string); // true
regex2.lastIndex; // 0
regex2.test(string); // true
regex2.lastIndex; // 9
regex2.test(string); // flase
regex2.lastIndex; // 0
regex2.test(string); // true

测试

匹配所有符合 XML 规则的标签

1
2
3
4
5
6
7
const regex = new RegExp(/<(\w+)>.+<\/(\1)>/);

regex.test("<div>code</div>"); // true
regex.test("<span>I Love U</span>"); // true
regex.test("<h1>This is title</p>"); // false
regex.test("<p>1</p>"); // true
regex.test("<p></p>"); // false

匹配所有的小数

1
2
3
4
5
6
7
const regex = new RegExp(/(?<!\.)\d+\.\d+$/);

regex.test(0.1); // true
regex.test(1.3); // true
regex.test(13.14); // true
regex.test("1.3.1.4"); // false
regex.test(1); // false

提取下列数据中所有人的生日,使用两个分组,第一个分组提取“月”,第二个分组提取“日”。

  • 王伟 1993 年 1 月 2 日
  • 张伟 1996.8.24
  • 李伟 1996.3.21
  • 李秀 1994-7-5
1
2
3
4
5
6
const regex = new RegExp(/((?<=[年.-])\d{1,2})[月.-](\d{1,2})/);

regex.exec("王伟 1993年1月2日");
// ['1月2', '1', '2', index: 8, input: '王伟 1993年1月2日', groups: undefined]
regex.exec("李伟 1996.3.21");
// ['3.21', '3', '21', index: 8, input: '李伟 1996.3.21', groups: undefined]

编写正则表达式进行密码强度的验证,规则如下:

  • 至少一个大写字母
  • 至少一个小写字母
  • 至少一个数字
  • 至少 8 个字符
1
2
3
4
5
6
7
const regex = new RegExp(/(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9]).{8,}/);

regex.test("123456789"); // false
regex.test("12ABab"); // false
regex.test("12345ABCabc"); // true
regex.test("ADMIN1234()"); // false
regex.test("Hmm5201314"); // true

实现一个模板引擎,能够满足如下场景使用:

  • let template = 我是,年龄,性别;
  • let data = { name: ‘姓名’, age: 18 }
  • render(template, data);
  • // 我是姓名,年龄 18,性别 undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const render1 = (template, data) => {
let arr,
res = "";
const regex = new RegExp(/([^ \w\s\{,]+)\{{2}([a-z]{1,})\}{2}/, "g");
while ((arr = regex.exec(template)) !== null) {
res += `${arr[1]}${data[arr[2]]},`;
}
console.log(res.slice(0, -2));
};
function render2(template, data) {
if (typeof template !== "string" || typeof data !== "object") {
return null;
}
return template.replace(/{{(.*?)}}/g, (match, $1) => data[$1]);
}

写一个方法把下划线命名转成大驼峰命名

1
2
3
function strToCamel(str) {
return str.replace(/(^|_)(\w)/g, (m, $1, $2) => $2.toUpperCase());
}

JavaScript之RegExp与正则
https://xuekeven.github.io/2022/12/10/JavaScript之RegExp与正则/
作者
Keven
发布于
2022年12月10日
更新于
2025年8月21日
许可协议