使用正则,我们可以进行字符串的查找、替换、验证、分割等
一、正则表达式创建方式
有两种创建方式:字面量创建方式、构造函数创建方式
1.1、字面量创建方式
- 使用/.../的方式创建
- 其中不能接收变量
let str = 'abc123abc123aakjsdh15';
let reg = /abc/g;
console.log(str.match(reg)); // ['abc', 'abc']
! 以上示例中的/abc/g代表全局匹配字符串"abc"
!字面量创建方式不能接收变量:
let str = 'abc123abc123aakjsdh15';
let abc = '123';
let reg = /abc/g;
console.log(str.match(reg)); // ['abc', 'abc'] 依旧表示全局匹配"abc"而不是"123"
1.2、构造函数创建方式
- 语法:let reg = new RegExp('表达式', 匹配模式)
- 遇到'\'要转义为'\\'
- 可接收变量
let str = 'abc123abc123aakjsdh15';
let reg = new RegExp('\\d', 'g'); // 表示全局匹配数字(\d表示数字,在表达式中\要转义为\\)g表示全局匹配
console.log(str.match(reg)); // ['1', '2', '3', '1', '2', '3', '1', '5']
二、正则匹配方法
2.1、正则对象相关方法
2.1.1、test
测试该字符串是否符合正则表达式,返回true/false
let str = '123asd890sdd1';
let reg= /\d+/g; // 表示匹配全局是1个或多个数字
console.log(reg.test(str)); // true
2.1.2、exec
- 执行后返回匹配结果(存在则返回结果,不存在则返回null)
- 执行结果与reg.lastIndex(下一次匹配的起始位置)有关
- 如果第一次执行,那么就返回第一次匹配结果
- 再次执行会从剩余字符串中进行匹配
- 直到匹配完毕返回null..
let str = '123asd890sdd1';
let reg1 = /\d+/g;
/*
[0: "123"
groups: undefined
index: 0
input: "123asd890sdd1"
length: 1]
*/
console.log(reg1.exec(str));
console.log(reg1.lastIndex);// 3
/*
[0: "890"
groups: undefined
index: 6
input: "123asd890sdd1"
length: 1]
*/
console.log(reg1.exec(str));
console.log(reg1.lastIndex);// 9
/*[0: "1"
groups: undefined
index: 12
input: "123asd890sdd1"
length: 1]
*/
console.log(reg1.exec(str));
console.log(reg1.lastIndex);// 13
console.log(reg1.exec(str)); // null
console.log(reg1.lastIndex);// 0
!注意 如果执行了 reg.test后,再执行reg.exec,就会从第一次匹配的结果往后去匹配
let str = '123asd890sdd1';
let reg1 = /\d+/g;
console.log(reg1.test(str));
console.log(reg1.exec(str));// ['890', index: 6, input: '123asd890sdd1', groups: undefined]
console.log(reg1.lastIndex);// 9
console.log(reg1.exec(str));// ['1', index: 12, input: '123asd890sdd1', groups: undefined]
console.log(reg1.lastIndex);// 13
console.log(reg1.exec(str));// null
console.log(reg1.lastIndex);// 0
2.2、字符串相关方法
2.2.1、split
以传递的正则所匹配的内容进行字符串的分割
let str = '123asd890sdd1';
console.log(str.split(/\d+/));// ["", "asd", "sdd", ""]
console.log(str.split(/\d/));// ['', '', '', 'asd', '', '', 'sdd', '']
// 要注意!开头已经是数字了会分割出来一个空字符串,
// 结尾也是数字,也会有一个空字符串
2.2.2、search
查找符合正则的第一个匹配内容的索引
let str = '123asd890sdd1';
console.log(str.search(/\D+/g));// 3 匹配第一个非数字内容的索引
2.2.3、match
- 如果是非全局匹配,显示的就是像exec似的,显示一组详细信息
- 如果是全局匹配,会返回一个数组
- 未找到则返回null
let str = '123asd123sdd1';
let reg2 = /123/
console.log(str.match(reg2)); // ['123', index: 0, input: '123asd123sdd1', groups: undefined]
let reg3 = /123/g
console.log(str.match(reg3));// ['123', '123']
2.2.4、replace
- replace第一个参数可以接收一个正则表达式,去替换正则表达式所匹配到的内容
- 返回替换后的新字符串,不会影响原来字符串
- 第二个参数除了传递目标字符串外,还可以传递一个回调函数
- 回调函数接收三个值:
- 第一个值表示匹配到的字符串
- 第二个值表示分组
- 第三个值表示索引
let str = '123asd123sdd1';
let reg4 = /\d/g;
console.log(str.replace(reg4, '*')); // ***asd***sdd*
let result = str.replace(reg4, function(arg){
return '*';
})
console.log(result);// ***asd***sdd*
三、元字符
3-1、字符相关
let str = '123_';
let reg = /\w+/g;
console.log(reg.test(str));// true
let reg2 = /\W+/g;
console.log(reg2.test(str));// false
let str2 = '123';
let reg3 = /\D+/g;
console.log(reg3.test(str2)); // false
let reg4 = /./;
console.log(reg4.test(str2));// true
console.log(reg4.test('\u2028')); // false
let str3 = `a
b`;
let reg5 = /a.b/; // 匹配a+非回车换行符号+b,模板字符串会保留换行,可见不会匹配到a.b
console.log(reg5.test(str3));// false
3-2、数量相关
let str4 = 'abcdeeeffg';
let reg6 = /cde{3}/g;
// 匹配cdeee(3个e)
console.log(reg6.test(str4)); // true
let reg7 = /cde{1,4}/g; // 出现1~4次都可以
console.log(reg7.test(str4));// true
let reg8 = /cde{1,}f/;
console.log(reg8.test(str4)); // true
let str5 = 'my name is lilei';
let reg9 = /my\s+name/g; // 表示my后面匹配一次或无限多次空格然后后面跟name
console.log(reg9.test(str5)); // true
其中,?还可以用来将贪婪匹配改正惰性匹配:
- 贪婪匹配
即:按照最多的数量去匹配
- 惰性匹配
即:按照最少的数量去匹配
比如:我们想匹配最少两个数字,最多4个
let str = '123456789';
let reg1 = /\d{2,4}/g;
console.log(str.match(reg1)); // ['1234', '5678']
let reg2 = /\d{2,4}?/g;
console.log(str.match(reg2));// ['12','34','56','78']
3-3、位置相关
let str6 = '123basdzx';
let reg10 = /^\w/g;
// 将开头的第一个字母数字或下划线换成&,如果开头不是字母数字或下划线则不替换
let ress = str6.replace(reg10, '&');
console.log(ress);//&23basdzx
let reg11 = /\w$/g;
// 将结尾的最后一个字母数字或下划线换成&, 如果结尾不是字母数字或下划线则不替换
let resss = str6.replace(reg11, '&');
console.log(resss);//123basdz&
let str7 = 'this is a book';
// 只匹配前后都是空格的is
let reg14 = /\bis\b/g;
let ressss = str7.match(reg14);
console.log(ressss);// ['is']
// 匹配左边非边界右边有边界的2个长度的字母数字或下划线
let reg15 = /\B\w{2}\b/g;
let res1 = str7.match(reg15);
console.log(res1);// ["is", "ok"]
3-4、括号相关
3-4-1、()的使用
- 使用()分组:
-
- 使用()分组后,后面再加{n}数量,则表示有几组该内容
let str = 'abababfdsafsa';
// 匹配三次'ab', 如果不加括号则是匹配abbb
let reg = /(ab){3}/g;
console.log(reg.test(str)); // true
-
使用()提取值:
- 使用()分组后,可结合字符串的match方法来进行内容提取,提取内容的方式有2种:
- str.match(reg)[index]
- RegExp.$n
- 使用()分组后,可结合字符串的match方法来进行内容提取,提取内容的方式有2种:
let str2 = '2021-12-21';
let reg2 = /(\d{4})-(\d{2})-(\d{2})/;
/*
0: "2021-12-21"
1: "2021"
2: "12"
3: "21"
groups: undefined
index: 0
input: "2021-12-21"
*/
console.log(str2.match(reg2));
console.log(str2.match(reg2)[1]); //2021
// 也可以用RegExp中的$1 $2 ... 来提取值
console.log(RegExp.$1);// 2021
console.log(RegExp.$2);// 12
- 使用()来替换内容:
-
使用()分组后,可结合字符串的replace方法进行替换,其中有两种替换方式:
- str.replace(reg, "$n") $n表示匹配到的分组内容,n表示第几组
- str.replace(reg, function(arg, val1,val2, ...)) val1,val2...分别表示匹配到的第几组的内容
-
let str2 = '2021-12-21';
let reg2 = /(\d{4})-(\d{2})-(\d{2})/;
// 用分组来替换成指定字符串
let res = str2.replace(reg2, '$2/$3/$1');
console.log(res); // 12/21/2021
//也可以用回调函数的形式
let ress = str2.replace(reg2,function(arg, year,month,date){
return `${month}/${date}/${year}`;
})
console.log(ress);// 12/21/2021
- 使用()来反向引用:
let className = 'news_container-nav';
// 比如我们要匹配中间的链接符号必须统一为_或-要保持一致
// 此时我们就要使用反向引用,比如前面匹配到的是_后面也要用_链接
let reg3 = /\w{4}(_|-)\w{9}(\1)\w{3}/;
// \n表示反向引用第几组的匹配结果 \1则表示反向引用第一组的匹配结果
console.log(reg3.test(className)); // false
3-4-2、[]的使用
- 使用[]表示或者
- 比如,[Ll]表示内容是L或者l都可以,也可以使用(L|l)表示
- ()中表示“或者”需要在内容中加上“|”,而[]表示或者就不需要加“|”
let strs = 'My name is LiLei';
// 匹配Lilei中的lei的l可以是 L也可以是l
// let regg = /Li(L|l)ei/g; // 可以使用小括号
let regg = /Li[Ll]ei/g; // 在中括号里就不用加|了,本来就代表或者的关系
console.log(regg.test(strs)); // true
-
使用[]表示一段内容
- []表示一段内容时,其中的^表示“非”的意思,而不是“开头”的意思
let strs = 'My name is LiLei';
let regs = /[^0-9]/g; // 非数字
console.log(regs.test(strs)); // true
3-4-2、{}的使用
详见3-2
3-5、匹配模式
- 在使用多行匹配m时,要同时加上g
let str = `abc
efg
hij`;
let reg = /^\w/gm; // 如果多行匹配开头,注意不要忘了g!!
let res = str.replace(reg, '*');
/*
*bc
*fg
*ij
*/
console.log(res);
-
s可以让'.'匹配到换行
- 由于'.'会匹配非换行符、非回车符、非段落结束符和非行结束符,所以要想让'.'匹配到换行符,就要加上s
let str = `abc
efg
hij`;
// let reg2 = /^a.*j$/g; // 由于.表示非换行 非回车 非段落结束符 非行结束符,所以这样就匹配不到了,因为换行了
let reg2 = /^a.*j$/gs; // a开头j结尾,中间任意多个非..符号,但是忽略换行符
console.log(reg2.test(str)); // true
- 使用u进行匹配unicode编码时,要配合\u与{}使用
- 当\u与{}连用时,{}中的数字表示unicode编码
let str3 = 'a';
// a的unicode编码是61,{}与\u连用,{}中的数字表示unicode编码值
let reg3 = /\u{61}/gu; // 加上u表示匹配unicode编码
console.log(reg3.test(str3)); // true
-
使用y开启粘性模式
- 开启粘性模式后,匹配到的内容必须是连续的
let ss = '12asd123';
let regg = /\d/gy;
console.log(regg.exec(ss));// ['1', index: 0, input: '12asd123', groups: undefined]
console.log(regg.exec(ss));// ['2', index: 1, input: '12asd123', groups: undefined]
// 如果不开启粘性模式,这里输出1
console.log(regg.exec(ss)); // null 因为开启了粘性模式,必须是连续的,所以执行到第三次为字母了就输出了null
四、命名分组
利用()分组后,可使用?<名称>来给分组起名
let str = '2021-12-21';
let reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
/*
0: "2021-12-21"
1: "2021"
2: "12"
3: "21"
groups:
day: "21"
month: "12"
year: "2021"
index: 0
input: "2021-12-21"
*/
// 可见,使用了命名分组后,groups中就拥有了命名和对应的值
console.log(str.match(reg));
console.log(str.match(reg).groups.day); // 21
五、零宽断言
5-1、正向零宽断言
let ss = 'iphone3iphone4iphone5iphonenumber';
// 希望匹配到iphone+数字,但是又希望替换iphone为“苹果”,又不希望替换数字
// 肯定的。匹配iphone后面是数字的
let regss = /iphone(?=\d)/g;
// 否定的。匹配iphone后面不是数字的
let regsss = /iphone(?!\d)/g;
console.log(ss.replace(regss,'苹果'));// 苹果3苹果4苹果5iphonenumber
console.log(ss.replace(regsss, '苹果'));// iphone3iphone4iphone5苹果number
5-2、负向零宽断言
let sss = '10px20px30pxipx';
// 肯定的。希望把px换成‘像素’,而数字不动
let gg = /(?<=\d+)px/g;
// 否定的。希望把px换成‘像素’,而前面是非数字的
let ggg = /(?<!\d+)px/g;
console.log(sss.replace(gg,'像素')); // 10像素20像素30像素ipx
console.log(sss.replace(ggg,'像素')); // 10px20px30pxi像素