JavaScript之RegExp与正则

本文最后更新于 2025年8月4日 晚上

参考:

  1. https://juejin.cn/post/7153800580077453326。
  2. https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp。

正则的核心

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

下图代表的是一个字符串: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
/^李.+/.test("李逍遥"); // true
/^李.+/.test("李"); // false
/^李.+/.test("慕容李逍遥"); // false
/(?<=[\s]?)(?<!\S)李.+/.test("李逍遥"); // true // "李" 左侧可以为空格不能为非空格字符

匹配字符

字符

模式 说明
字母、数字 匹配字符本身
\0 匹配 NUL 字符
\t 匹配水平制表符
\v 匹配垂直制表符
\n 匹配换行符
\r 匹配回车符
\f 匹配换页符
\xnn 匹配拉丁字符 nn
\uxxxx 匹配 Unicode 字符 xxxx
\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/; // true
regex.test("javascript"); // true
regex.test("Javascript"); // true

const regex1 = /[jJ]ava/; // true
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,} 至少连续出现 n 次(贪婪模式)
{n} 连续出现 n 次(贪婪模式)
? 等价于 {0,1}(贪婪模式)
+ 等价于 {1,}(贪婪模式)
* 等价于 {0,}(贪婪模式)
{n,m}? 连续出现 n 到 m 次(惰性模式)
{n,}? 至少连续出现 n 次(惰性模式)
{n}? 连续出现 n 次(惰性模式)
?? 等价于 {0,1}?(惰性模式)
+? 等价于 {1,}?(惰性模式)
*? 等价于 {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.test("朋友.mp3");
// null
regex.test("学习资料.rmvb");
// ['学习资料.rmvb', '.rmvb', index: 0, input: '学习资料.rmvb', groups: undefined]

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月4日
许可协议