底层
构造函数
构造函数
- 作用:构造一个正则表达式
- 调用:new RegExp(pattern[, flags])
- 入参:String|RegExp [, String]
- 返回:RegExp
- tip:flags支持的标志有:g / i / m / s / u / y
字面量和构造函数
- 有两种方式构造正则:字面量和构造器
- 区别:
- 字面量赋值是编译状态,效率更高。
- 构造函数是运行时编译,效率会更低,但支持传入字符串进行运行时解析,所以可以有更自由的定义正则方式。
const str = 'www.music.qq.com/'
const reg1 = /qq.com/g;
const reg2 = new RegExp(/qq.com/, 'g');
console.log(reg1.test(str));
console.log(reg2.test(str));
1
2
3
4
5
6
falgs语法
flags
flag | 作用 |
g | 全局匹配 |
i | 忽略大小写 |
m | 多行匹配模式:开头匹配(^)/结尾匹配($)按照每行匹配。(以\n或者\r 分隔) |
s | 点匹配所有字符(包括换行字符) |
u | unicode模式匹配 |
y | 粘性匹配,从lastIndex匹配,且不会向下搜索 |
const reg = /./gimsuy
console.log(reg.flags);
1
2
- 全局匹配,exec/test启用lastIndex
const reg = /1/g;
const str = '121';
console.log(reg.exec(str));
console.log(reg.exec(str));
console.log(reg.exec(str));
1
2
3
4
5
- 对search等无效。matchAll必须加g,否则报错
const reg = /1/g;
const str = '121';
console.log(str.search(reg));
console.log(str.search(reg));
console.log(str.search(reg));
1
2
3
4
5
6
const reg1 = /HELLO/i;
const reg2 = /HELLO/;
const str = 'hello world!';
console.log(reg1.test(str));
console.log(reg2.test(str));
1
2
3
4
5
6
- 多行匹配模式
- 【^】和【$】默认是匹配全文的头尾
- 加m后匹配的是每一行得头尾
const reg1 = /^h/m;
const reg2 = /^h/;
const str = `I am
happy
!`
console.log(reg1.test(str));
console.log(reg2.test(str));
1
2
3
4
5
6
7
- '.'是否匹配换行符:
- U+000A 换行符("\n")
- U+000D 回车符("\r")
- U+2028 行分隔符(line separator)
- U+2029 段分隔符(paragraph separator)
const reg1 = /./g;
const reg2 = /./sg;
const str = `1
2
3`;
console.log(str.match(reg1));
console.log(str.match(reg2));
1
2
3
4
5
6
7
let text = "安㑝㑞㑟㑠㑡㑢";
let reg1 = /[\W]+/g;
let reg2 = /[\u4E00-\u9FA5]+/g;
console.log(reg1.exec(text));
console.log(reg2.exec(text));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- stikey:粘性,代表从lastIndex的第一个字符开始就需要能匹配到pattern
- 如果匹配到了,lastIndex = 匹配的最后一个字符下标 + 1
- 如果未匹配到,就将lastIndex重置为0
const str = 'apa';
const reg = /a/y;
console.log(reg.exec(str));
console.log(reg.lastIndex);
console.log(reg.exec(str));
console.log(reg.lastIndex);
1
2
3
4
5
6
7
8
9
10
- 对比非粘性:
- 非粘性不会动lastIndex。test、exec一直都是从0开始匹配
- 且不必第一个字符就匹配到,可以向下搜索匹配
const str = 'pa';
const reg2 = /a/;
console.log(reg2.test(str));
console.log(reg2.test(str));
console.log(reg2.test(str));
1
2
3
4
5
6
pattern模式
基础语法
- 【+】匹配一个到多个
- 【*】匹配零个到多个
- 【?】匹配零个到一个
const str = 'aab';
const reg1 = /a/g;
console.log(str.match(reg1));
const reg2 = /a+/g;
console.log(str.match(reg2));
const reg3 = /a*/g;
console.log(str.match(reg3));
const reg4 = /a?/g;
console.log(str.match(reg4));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 【a+】匹配最大数量的a
- 【a+?】匹配最少数量(不包括0)的a
- 【a*】匹配最大数量的a
- 【a*?】匹配最少数量(包括0)的a
const str = 'aab';
const reg1 = /a+/g;
console.log(str.match(reg1));
const reg2 = /a+?/g;
console.log(str.match(reg2));
const reg3 = /a*/g;
console.log(str.match(reg3));
const reg4 = /a*?/g;
console.log(str.match(reg4));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 字符和中括号表达式只能取一个匹配值,要限制取值个数需要加限定符
- 用贪婪/非贪婪限制。例:取所有单词【[a-zA-Z]+】
- 用【{}】限制
- 【[a-z]{2}】 匹配2个小写字符
- 【[a-z]{2,}】匹配2个以上小写字符
- 【[a-z]{2,4}】匹配2-4个小写字符
- 【[a-z]{1,4}】匹配4个以下小写字符
const str = 'aDcdEfghi';
const reg1 = /[a-z]{2}/g;
console.log(str.match(reg1));
const reg2 = /[a-z]{2,}/g;
console.log(str.match(reg2));
const reg3 = /[a-z]{2,4}/g;
console.log(str.match(reg3));
const reg4 = /[a-z]{1,4}/g;
console.log(str.match(reg4));
1
2
3
4
5
6
7
8
9
10
11
12
13
- 【[ ]】
中括号表达式
,可以限制值的范围。例如,匹配一个小写字母:[a-z] - 【[-]】可以取数字、小写字母、大写字母的
区间
。例如,匹配abc中的一个:[a-c] - 【[^]】可以
取反范围
。例如,匹配一个非小写字母:[^a-z] - 【[|]】可以
表示选择范围
。例如,匹配字母a或C:[a|C]
const str = 'abCD';
const reg1 = /[a-z]/g;
console.log(str.match(reg1));
const reg2 = /[^a-z]/g;
console.log(str.match(reg2));
const reg3 = /[a|C]/g;
console.log(str.match(reg3));
const reg4 = /[^a|C]/g;
console.log(str.match(reg4));
1
2
3
4
5
6
7
8
9
10
11
12
13
非打印字符 | 含义 |
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 制表符 |
\v | 垂直制表符 |
- 【\s】匹配空白和非打印字符。等价于 [ \f\n\r\t\v]
- 【\S】匹配非空白的字符们。等价于 [^ \f\n\r\t\v]
const reg1 = /\s/g;
const reg2 = /\S+/g;
const str = `Hello
world !`
console.log(str.match(reg1));
console.log(str.match(reg2));
1
2
3
4
5
6
7
- 【\b】单词间的间距。
- 【\B】字母间的间距。
- 【\w】匹配所有字母数字下划线。相当于:[a-zA-Z0-9_]
- 【\W】匹配所有非字母数字下划线。相当于:[^a-zA-Z0-9_]
- 【\d】匹配一个数字
- 【\D】匹配一个非数字
const str = '1万_1W';
const reg1 = /\w/g;
const reg2 = /[a-zA-Z0-9_]/g;
console.log(str.match(reg1));
console.log(str.match(reg2));
const reg3 = /\W/g;
const reg4 = /[^a-zA-Z0-9_]/g;
console.log(str.match(reg3));
console.log(str.match(reg4));
1
2
3
4
5
6
7
8
9
10
11
12
const str = '123sf';
const reg = /\d/g;
const reg2 = /\D/g;
console.log(str.match(reg));
console.log(str.match(reg2));
1
2
3
4
5
6
- m标识符可以让每行都匹配起始结束符
- 【^】起始位置。如整个字符串以h起始:^h
- 【$】结束位置。如整个字符串以!结束:!$
- 【[^]】控制起始字符起始。如找到以h起始的单词位置:/[^h]/g
- 【[$]】控制结束字符起始。如找到以!结束的单词位置:/[!$]/g
const str = `hello!
hdy!`
const reg1 = /^h\S*/g;
console.log(str.match(reg1));
const reg2 = /\S*!$/g;
console.log(str.match(reg2));
const reg3 = /[\b^h]\S+/g;
console.log(str.match(reg3));
const reg4 = /\S*[!$]/g;
console.log(str.match(reg4));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- 使用【()】括起来的匹配项会被单独保存成一个匹配组。例:匹配abc的同时想拿到b:/a(b)c/g
const str = 'I am abc';
const reg1 = /a(b)c/;
console.log(str.match(reg1));
console.log(str.match(reg1)[1]);
const reg2 = /a(b)c/g;
console.log(reg2.exec(str)[1]);
1
2
3
4
5
6
7
8
9
10
const str = 'I am abc';
const reg2 = /a((b)c)/g;
console.log(reg2.exec(str));
1
2
3
const str = 'I am abc';
const reg1 = /a(b)c/g;
const reg2 = /a(?:b)c/g;
console.log(reg1.exec(str));
console.log(reg2.exec(str));
1
2
3
4
5
高级筛选
又名零宽断言
,ios / mac部分不支持
- 【exp1(?=exp2)】:查找 exp2 前面的 exp1。
- 【exp1(?!exp2)】:查找后面不是 exp2 的 exp1。
- 【(?<=exp2)exp1】:查找前面是 exp2 的 exp1,safari不支持。
- 【(?<!exp2)exp1】:查找前面不是 exp2 的 exp1,safari不支持。
const str = '986005715@qq.com';
const reg1 = /[\w]+?(?=@)/g;
console.log(str.match(reg1));
const reg2 = /(?<=@)[\w\.]+/g;
console.log(str.match(reg2));
1
2
3
4
5
6
7
8
9
需要用括号语法来匹配,但是不希望它存分组
const str = `
const zs = {name: '张三', age: 20};
const me = {name: 'hdy', age: 18};
`;
const reg = /(me\s*=\s*)\{[^\}]+\}/g;
console.log(reg.exec(str));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const str = `
const zs = {name: '张三', age: 20};
const me = {name: 'hdy', age: 18};
`;
const reg = /(?:me\s*=\s*)\{[^\}]+\}/g;
console.log(reg.exec(str));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const str = `
const zs = {name: '张三', age: 20};
const me = {name: 'hdy', age: 18};
`;
const reg = /(?<=me\s*=\s*)\{[^\}]+\}/g;
console.log(reg.exec(str));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
可以使用前面已经匹配到的项
const str = 'aabbccc';
const reg = /(\w)\1+/g;
console.log(str.match(reg));
console.log(str.replace(reg, '$1'));
1
2
3
4
const str = 'Hello my my name is is hdy~';
const reg = /(\b\w+\b)\s+\1+/g;
console.log(str.replace(reg, '$1'));
1
2
3
主动执行方法
规则
- 如果一个表达式同时指定了 sticky 和 global,其将会忽略 global 标志。
- sticky不会更改lastIndex,除非匹配结束,重置lastIndex
exec
- 作用:逐条遍历匹配项(在加g/y的情况下)
- 调用:reg.exec(str)
- 入参:String
- 返回:Array | null
exec对比match
- exec和match都可以拿到所有匹配项
- exec可以逐条遍历,match只是返回匹配数组
const str = 'aaple';
const reg2 = /a/g;
console.log(reg2.exec(str));
console.log(Array.isArray(reg2.exec(str)));
1
2
3
4
5
const str = 'aaple';
const reg2 = /a/g;
console.log(reg2.exec(str));
console.log(reg2.exec(str));
console.log(reg2.exec(str));
console.log(reg2.exec(str));
1
2
3
4
5
6
7
const str = 'aaple';
const reg = /a(p?)/g;
console.log(reg.exec(str));
console.log(reg.exec(str));
console.log(reg.exec(str));
1
2
3
4
5
6
const str = 'hello world!';
const reg = /l/g;
console.log(str.match(reg));
console.log(reg.exec(str));
console.log(reg.exec(str));
console.log(reg.exec(str));
console.log(reg.exec(str));
1
2
3
4
5
6
7
8
9
10
11
- 不加g/y它就不会更改lastIndex,lastIndex一直是0,那么就一直从0开始查找
const str = 'hello world!';
const reg = /l/;
console.log(reg.exec(str));
console.log(reg.exec(str));
1
2
3
4
5
test
- 作用:检测字符串是否由匹配项
- 调用:reg.test(str)
- 入参:String
- 返回:Boolean
const reg = /apple/;
const str = 'I like apple';
console.log(reg.test(str));
1
2
3
- search返回Number,test返回Boolean,indexOf可以指定开始下标
- test在全局或粘性匹配模式会使用lastIndex可进行遍历匹配
const str = '1234';
const reg = /1/;
console.log(str.search(reg));
console.log(reg.test(str));
1
2
3
4
5
- 粘性y每次匹配必须在lastIndex处匹配到,否则就算失败,lastIndex重置0。
const str = '121111';
const reg = /1/y;
console.log(reg.test(str));
console.log(reg.lastIndex);
console.log(reg.test(str));
console.log(reg.lastIndex);
console.log(reg.test(str));
console.log(reg.lastIndex);
1
2
3
4
5
6
7
8
9
10
11
- 全局模式也是会从lastIndex开始匹配,会向下搜索,搜索完毕为null会重置lastIndex
const reg = /1/g;
const str = '1211';
console.log(reg.test(str));
console.log(reg.test(str));
console.log(reg.test(str));
console.log(reg.test(str));
console.log(reg.test(str));
1
2
3
4
5
6
7
8
toString
- 作用:以字符串形式返回正则表达式
- 调用:reg.toString()
- 返回:String
const reg = /abc/gmi;
console.log(reg.toString());
1
2
例子
- commonjs匹配依赖文件路径的简单写法
- str文件依赖了【./a】【./b】文件
const str = `
console.log('main run');
const a = require('./a');
const b = require('./b');
a.run();
b.run();
`
const reg = /(?<=require\(['"])[^['"]+(?=['"]\))/g;
const requireArr = [];
while((ans = reg.exec(str)) != null) {
requireArr.push(ans[0]);
}
console.log(requireArr);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
属性
lastIndex
- 作用:记录全局匹配模式下,上一次匹配的下标 + 1
- 使用:reg.lastIndex
- 值:Number
- 注:全局匹配模式(g)/粘性模式(y) 会启用此属性
const str = 'hello';
const reg = /o/g;
console.log(reg.exec(str));
console.log(reg.lastIndex);
console.log(reg.exec(str));
console.log(reg.lastIndex);
console.log(reg.exec(str));
console.log(reg.lastIndex);
1
2
3
4
5
6
7
8
9
10
11
12
13
source
- 作用:拿到正则的pattern(不包括falgs)
- 使用:reg.source
- 值:String
const reg = /aaa/gim;
console.log(reg.source);
1
2
dotAll
- 作用:查看是否使用了标识符【s】
- 调用:reg.dotAll
- 值:Boolean
const reg1 = /./;
const reg2 = /./s;
console.log(reg1.dotAll);
console.log(reg2.dotAll);
1
2
3
4
5
global
- 作用:查看是否使用了标识符【g】
- 使用:reg.global
- 值:Boolean
const reg1 = /./ig;
const reg2 = /./i;
console.log(reg1.global);
console.log(reg2.global);
1
2
3
4
5
ignoreCase
- 作用:查看是否使用了标识符【i】
- 使用:reg.ignoreCase
- 值:Boolean
const reg1 = /i/g;
const reg2 = /i/ig;
console.log(reg1.ignoreCase);
console.log(reg2.ignoreCase);
1
2
3
4
5
multiline
- 作用:查看是否使用了标识符【m】
- 使用:reg.multiline
- 值:Boolean
const reg1 = /m/m;
const reg2 = /m/;
console.log(reg1.multiline);
console.log(reg2.multiline);
1
2
3
4
5
sticky
- 作用:查看是否使用了标识符【y】
- 使用:reg.sticky
- 值:Boolean
const reg1 = /aa/y;
const reg2 = /aa/;
console.log(reg1.sticky);
console.log(reg2.sticky);
1
2
3
4
5
unicode
- 作用:查看是否使用了标识符【u】
- 调用:reg.unicode
- 值:Boolean
const reg1 = /a/u;
const reg2 = /a/;
console.log(reg1.unicode);
console.log(reg2.unicode);
1
2
3
4
5
flags
falgs
- 作用:拿到所有的标识符
- 调用:reg.flags
- 值:String
- tip:(g/i/s/m/u/y)且会自动按字母顺序排序
const reg = /./igsumy;
console.log(reg.flags);
1
2
const reg = /i/igsumy;
console.log(reg.myFlags);
1
2
3
4
Object.defineProperty(RegExp.prototype, 'myFlags', {
get() {
return this.toString().match(/[gsiumy]*$/)[0];
}
})
1
2
3
4
5
hasIndices
- tip:有版本限制
- 作用:查看是否使用了标识符【d】
- 调用:reg.hasIndices
- 值:Boolean
环境 | 支持版本 |
chrome | 90 |
node | no |
Symbol方法
- 被Symbol标识出来的方法,基本都被String.prototype.XXX 内部调用
- 如:RegExp.prototype[Symbol.match]被String.prototype.match()内部调用
match
- tip:该方法是被String.prototype.match()内部调用
- 调用:str1.match(reg)
- 入参:RegExp
- 返回:Array | Object
- tip:入参不是RegExp会被隐式 new RegExp(reg)
- tip:返回值类型取决于有没有加 g
let a = 'hello world!';
let b = /l/g;
let c = /l/;
console.log(a.match(b));
console.log(a.match(c));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- String.prototype.match()内部调用证明
const reg = /1/g;
const str = '123';
console.log(str.match(reg));
reg[Symbol.match] = (str) => ['嘿', '嘿嘿', '嘿嘿嘿']
console.log(str.match(reg));
1
2
3
4
5
6
7
matchAll
- tip:是被String.prototype.matchAll()内部调用
- 调用:str1.matchAll(reg)
- 入参:RegExp
- 返回:iterator,所有的捕获组的一个迭代器
- tip:入参正则必须设置g
let a = 'hello world!';
let b = /l/g;
let c = /l/;
console.log(a.matchAll(b));
console.log(a.matchAll(c));
console.log(Array.from(a.matchAll(b)));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
replace
- tip:由String.prototype.replace()内部调用
- 作用:替换(一个或全部)指定字符串
- 调用:str1.replace(reg, str2)
- 入参1:String | RegExp
- 入参2:String | (matchs[, match...], input) => {}
- 返回:String
- tip:不修改原str1
- tip:第一个参数是正则的时候,第二个参数可以是函数,函数入参取决于正则有没有分组
let a = 'hello world!';
console.log(a.replace('l', 'L'));
1
2
3
let a = 'hello world!';
let reg1 = /l/;
console.log(a.replace(reg1, (matchs, index, input) => {
console.log(matchs);
console.log(index);
console.log(input);
return 'L'
}));
1
2
3
4
5
6
7
8
9
let a = 'hello world!';
let reg2 = /l/g;
console.log(a.replace(reg2, (matchs, index, input) => {
console.log(matchs);
console.log(index);
console.log(input);
return 'L'
}));
1
2
3
4
5
6
7
8
9
10
11
12
let a = 'hello world!';
let reg3 = /l(.*?)w/;
console.log(a.replace(reg3, (matchs, match, index, input) => {
console.log(matchs);
console.log(match);
console.log(index);
console.log(input);
return 'L'
}));
1
2
3
4
5
6
7
8
9
10
11
12
replace可以使用前面已经匹配到的项,用$表示
const str = 'aabbccc';
const reg = /(\w)\1+/g;
console.log(str.match(reg));
console.log(str.replace(reg, '$1'));
1
2
3
4
const str = 'Hello my my name is is hdy~';
const reg = /(\b\w+\b)\s+\1+/g;
console.log(str.replace(reg, '$1'));
1
2
3
search
- tip:由String.prototype.search()内部调用
- 作用:查找字符串的第一个匹配项
- 调用:str.search(reg)
- 传参:RegExp
- 返回:Number
- tip:如果没找到会返回-1
var str = "hey JudE";
var re = /[A-Z]/g;
var re2 = /[.]/g;
console.log(str.search(re));
console.log(str.search(re2));
1
2
3
4
5
6
search和indexof的区别
- search支持正则,但也只能返回第一个匹配项
- indexOf支持变换起始下标,但不支持正则
const str = '1231';
console.log(str.search(/1/, 1));
console.log(str.indexOf('1', 1));
1
2
3
split
- tip:String.prototype.split()内部调用
- 调用:str1.split(str2|reg[, len])
- 入参:String | RegExp[, Number]
- 返回:Array,用str2/reg分割得来的数组,可限制数组长度
- tip:第二个参数是限制了数组的长度
- tip:第一个参数可以是正则
- 参数1是正则时,String.prototype.split()内部调用此方法
const reg = /1/g;
const str = '123';
console.log(str.split(reg));
reg[Symbol.split] = (str) => ['嘿', '嘿嘿', '嘿嘿嘿']
console.log(str.split(reg));
1
2
3
4
5
6
let a = 'hello';
console.log(a.split('l'));
console.log(a.split('l', 2));
1
2
3
4
const str = 'hello world !';
console.log(str.split(' ').join(''));
1
2
const str = 'hello world !';
const reg = /[\e]/;
console.log(str.split(reg));
1
2
3
4
species
class MyRegExp extends RegExp {
static get [Symbol.species]() { return RegExp; }
}
1
2
3
4