面试官:谈谈你了解的Js正则表达式

1,201 阅读10分钟

正则表达式,往往是很多前端开发者的一个小痛点,会却不精通,看得懂一点却不会写,很大一部分"cv工程师"遇上了各种正则校验时往往会选择直接百度一把梭,看似很爽,但实则没有真正弄清楚正则的匹配规则和方法,但其实正则在JS中还是很容易突破和掌握的希望大家看完这一篇文章,跟着里面的应用需求案例一个个搞定,以后遇上正则相关需求,能自己手动搞定,增强工作的成就感~我敢说,只要认真看完了,保证你有所收获!

如果对您有一丢丢帮助,希望能点个小赞咯~~

正则作用和意义

  • 字符串的检索及查找
  • 替换字符串
  • 验证
  • 分割

正则的创建

Js正则的创建一般有两种方式,如下:

1.字面量方式创建正则

    //检索出字符串中所有的asd
    let testStr = "asdasdff1223asdf234dff456"
    let testReg = /\d+/g;
    let result = testStr.match(testReg)
    console.log(result) // ['1223', '234', '456']
    

2.通过构造函数方式创建正则

    //检索出字符串中所有的 数字字段
    let testStr2 = "asdasdff1223asdf234dff456"
     //通过构造函数创建的正则,如果原先包含特殊字符比如"\"的,
     //需要额外加入反斜杠"\"
    let testReg2 =  new RegExp('\\d+','g') 
    let result2 = testStr2.match(testReg2)
    console.log(result2) // ['1223', '234', '456']
    
    //Tips:与字面量创建正则方式不同的是,通过构造函数创建的正则可以传变量,而不是只能传字符串。

正则的匹配方法

1.正则对象下包含的方法:

RegExp.test(String)

    //regObj.test(String) 查询。可以查询按正则匹配后的字符串是否存在于传入的字符串中
    let testStr = "asdasdff1223asdf234dff456"
    let testReg = /\d+/g;
    let result = testReg.test(testStr)
    console.log(result) // ['1223', '234', '456']

RegExp.exec(String)

    //regObj.exec(String)  查询并返回从上一次查询到的索引之后(没有上一次就从0开始)的第一个符合条件的字符串。
    let testStr = "asdasdff1223asdf234dff456"
    let testReg = /\d+/g;
    let result1 = testReg.exec(testStr) //['123']
    let result2 = testReg.exec(testStr) //['234']
    let result2 = testReg.exec(testStr) //['456']

2.字符串下包含的正则相关方法

String.split()

   //把字符串切割成数组,值得注意的是,该方法可以传入正则,通过正则匹配切割字符
   let testStr = "asdasdff1223asdf234dff456"
   let result = testStr.split(/\d+/) //['asdasdff', 'asdf', 'dff', '']

String.search()

    // 传入正则匹配符合条件的第一个字符在整个字符串中的索引,忽略全局匹配,这里传全局匹配符"g"无效
    let testStr = "asdasdff1223asdf234dff456"
    let result = testStr.search(/\d+/) 
    console.log(result) //8

String.match()

    //匹配传入字符串,输出符合条件的字符串数组(包含索引信息)
    let testStr = "asdasdff1223asdf234dff456"
    let result = testStr.match(/\d+/g) // ['1223', '234', '456']

String.replace()

    String.replace(要匹配的字符(可以是String,也可以是正则), 要替换成的字符 :String)
    //替换字符串中匹配到的子字符串,输出替换后的字符串
    let testStr = "asdasdff1223asdf234dff456"
    let result = testStr.replace(/\d+/g,'ppp')
    console.log(result)// 'asdasdffpppasdfpppdffppp'
    
     Tips: replace第二个参数除了能传要替换成的字符之外,还可以传回调函数,
     //检索到符合的字符则执行一次,里面的参数是在目标字符串中,匹配到的所有字符
     let testStr = "asdasdff1223asdf234dff456"
     let result = testStr.replace(/\d+/g,function(str){
     console.log(arguments) //['1223', 8, 'asdasdff1223asdf234dff456']
     //因为是全局匹配,回调函数会执行多次,这是第一次命中的的arguments
     //arguments第0项为本次检索到的字符串,
     //第1项为其在原字符串中索引,
     //第2项为原字符串
       
     console.log(str); // '1223' '234' '456'
     //总共匹配到3处符合条件的字符串,分别为上面3个
     return '***' //本次命中要替换成的字符为 ***  
   })
   console.log(result)  //asdasdff***asdf***dff***

元字符(核心)

     我们通常看不懂网友写的正则,或者遇上复杂的检索需求不会用正则解决,最重要的一点就是正则表达的元字符(具有不同匹配规则的各种特殊非字母字符)没有理解到位,把元字符搞定以后,遇上了写基础正则的需求大家都应该十拿十稳了。 为了方便记忆,我们把元字符分成以下几类归纳概述,并配上了应用场景配合理解,请放心食用:

1.字符串相关元字符

//记忆规则:小写字母往往是包含xxx,大写往往是不包含xxx(即小写的补集)
\w :匹配数字、字母、下划线;
    //应用场景:判断字符串是否包含数字、字母、下划线中的一种或多种
    let reg = /\w/;
    let testStr =  '~@4_a'
    let result = reg.test(testStr);
    console.log(result)  
\W :匹配非数字字母下划线;
    //应用场景: 验证输入的账号名是否有非字母、数字或下划线
    let testStr1 =  '_asdf!33e'
    let reg = /\W/;
    let result = reg.test(testStr);//true
\d :匹配数字;
    //应用场景:提取字符串中所有数字段
    let testStr = "asdasdff1223asdf234dff456"
    let testReg = /\d+/g;
    let result = testStr.match(testReg)
    console.log(result) // ['1223', '234', '456']
\D :匹配非数字;
    //应用场景:验证输入的年龄是否为非自然数,是则抛出提示
    let reg = /\D+/g;
    let testStr =  '23d'
    let result = reg.test(testStr);
    console.log(result) //true
\s :匹配空格;
    //应用场景:将输入的字符串中所有的空格移除
    let testStr = ' asdf 2423d 4f ';
    let reg = /\s+/g;
    let result = testStr.replace(reg,'');
    console.log(result) //asdf2423d4f
\S :匹配非空格;
.   :匹配非 \n(换行)、\r(回车)、\u2028(段落结束符)、\u2029(行结束符)

2.数量相关元字符

{} :表示有多个重复的字符 具体写法如下: 
let testStr = 'asdfffffff234ad';
reg1 = /df{2}/g;  // 2个f 字符串是否包含'dff'
reg2= /df{1,3}/g; // 1-3个f(包括1和3), 字符串是否包含'df'或'dff'或'dfff'
reg3= /df{2,}/g;  // 2个及2个以上f, 字符串是否包含'dff'或'dfff...'
let result = reg3.test(testStr); //true

? :等同于{0,1},是其简写。//?还有一个作用,是开启惰性匹配(默认为贪婪匹配)
* :等同于{0,},是其简写。
+ :等同于{1,},是其简写。

3.匹配位置相关元字符

^ :匹配输入字符串的开始位置,如果后面接了字符xx,代表匹配以xx开头的字符串。
    //应用场景:判断字符串是否以数字开头
    let strTest = "3434sdfsdf"
    let regTest = /^\d/;
    console.log(regTest.test(strTest))
    //tips:但在字符集合"[]"(下文会提到)中,"^"也有非,取补集的意思。
$ :匹配输入字符串的结束位置,如果"$"前面拼接了字符xx,代表匹配以xx结尾的字符串
   //应用场景1:在指定字符串后凭借一串字符'xxx'
    let strTest = 'asdf334';
    const regTest = /$/;
    let result = strTest.replace(regTest,'xxx');
    console.log(result) // 'asdf334xxx'
   //应用场景2:判断字符串是否以字母、数字、下划线结尾
    let strTest = 'asdf334';
    const regTest = /\w$/;
    let result = regTest.test(strTest)
    console.log(result) //true
\b :边界符,匹配带边界的字符串 
    //边界:指非\w的字符即(非字母、数字、下划线都算边界,比如一个字符串是一个独立的单词那么它左右两边都是边界)
    //应用场景:在指定字符串中找出独立的词'he',而非被混合进其他词的'he'字符串
    let strTest = 'she or he?';
    const regTest1 = /he/g; //这里会匹配到2个'he'
    const regTest2 = /\bhe\b/g; //这里只会匹配到独立的单词'he' 
    let result1 = strTest.match(regTest1) //['he', 'he']
    let result2 = strTest.match(regTest2) //['he']
    
\b :匹配到非边界的字符串
   //应用场景:找出 'she'中的'he'而非独立的单词'he';
    let strTest = 'she or he?';
    const regTest1 = /he/g; //这里会匹配到2个'he'
    const regTest2 = /\Bhe\b/g; //这里只会匹配到'she'单词中的'he' 
    let result1 = strTest.match(regTest1) //['he', 'he']
    let result2 = strTest.match(regTest2) //['he']

4.其他相关元字符(括号等)

() :可以用来分组、分组提取值、分组替换。学会以后可谓是妙蛙种子吃了妙脆角进到米奇妙妙屋,妙到家啦,妙啊O(∩_∩)O。
    1.分组:
    //应用场景 判断字符串里是否包含连续3个'sdf'字符串
    let strTest = 'aswdsdfsdfsdfeer223d';
    const regTest1 = /sdfsdfsdf/g; //这种方式明显不够优雅
    const regTest2 = /sdf{3}/g; //这里只会匹配到连续3个f
    const regTest3 = /(sdf){3}/g; //用括号包裹'sdf',才能把'sdf'进行分组,让编译器识别我们的意图
    let result1 = strTest.match(regTest1) //['sdfsdfsdf'] 
    let result2 = strTest.match(regTest2) //null
    let result3 = strTest.match(regTest3) //['sdfsdfsdf']
    2.分组提取值:
    //应用场景 提取出时间字符串里的年月日
    let strTest = '日期是2021-11-10日';
    const regTest1 = /\d{4}-\d{2}-\d{2}/; //这样只能匹配到 时间字符串 '2021-11-10',那如何单独获取年月日呢
    const regTest2 = /(\d{4})-(\d{2})-(\d{2})/; //利用小括号分组
    let result1 = strTest.match(regTest1) //['2021-11-10']
    let result2 = strTest.match(regTest2) //['2021-11-10', '2021', '11', '10']
    //Tips: 两种方式提取值:
    console.log('月份是:',result2[2]) //月份是:11
    console.log('日期是:',RegExp.$3) //日期是:10  //其中$3等同于result2[3]
    
    3.分组替换
    //应用场景 将字符串中YYYY-MM-DD 替换成DD/MM/YYYY
    let strTest = '日期是2021-11-10';
    const regTest = /(\d{4})-(\d{2})-(\d{2})/;
    //tips: 两种方式分组替换:
    let result1 = strTest.replace(regTest,function(arg,year,month,date){
        console.log(arg,arguments)
        return `${date}/${month}/${year}`
    }) //日期是10/11/2021
    let result2 = strTest.replace(regTest,'$3/$2/$1') //日期是10/11/2021
    4.反向引用
        //应用场景 判断类名字符串连接符是否统一为"-"或者'_'。(在类名字符串中,单词之间可能是'-'或者'_'连接)
    let strTest = 'main_content-title';
    const regTest = /\w{4}(-|_)\w{7}(-|_)\w{5}/; //这样能匹配到字符串,但是不能匹配到是否连字符统一为'-'或'_'
    const regTest2 = /\w{4}(-|_)\w{7}(\1)\w{5}/;//这里的"(\1)"代表第1个"()"内分组的使用同一种,第一个是-,那么里也得是-;反之,第一个是_,这里也必须是_
    
    let result1 = regTest.test(strTest); //true
    let result2 = regTest2.test(strTest);//false 因为两个分组(小括号)内不统一

字符集合

正则表达式中的字符集合一般指"[]"。

[] :用于查找某个范围内的字符
常见用法:
[a-z] :代表查找任何小写英文字母
[a-zA-Z] :代表查找任何大小写英文字母,其中不能写a-Z是因为a至Z不为连续的ASCII码而a-z是连续的ASCII码。
[0-9] :代表匹配数字,等同于\d Tips:[^0-9]代表匹配非数字,字符集合里的"^"代表非的意思,等同于\D
[0-9a-zA-Z_] :等同于\w

匹配模式

g :全局匹配模式,匹配到第一个时会继续检索,直到检索完整个字符串
    let strTest = 'asd234dddasdf234xxx';
    const regTest1 = /234/; 
    const regTest2 = /234/g; 
    let result1 = strTest.match(regTest1) //['234']
    let result2 = strTest.match(regTest2) //['234','234']
i :忽略大小写匹配模式
    let strTest = 'TianYaZhang';
    const regTest1 = /tianyazhang/; 
    const regTest2 = /tianyazhang/i; 
    let result1 = strTest.match(regTest1) // null
    let result2 = strTest.match(regTest2) //['TianYaZhang']
m :多行模式 
   //一般用于多行字符串下操作
       //需求:把字符串每行开头的字母变成大写
    let strTest = `tian
ya
zhang`;
    const regTest1 = /^\w/gm;
    let result = strTest.replace(regTest1,function(){
        return arguments[0].toLocaleUpperCase()
    }) 
    console.log(result) 
    //Tian
    //Ya
    //Zhang
  
 u :可以在正则中匹配unicode编码;
 y :粘性模式,字符串中只有连续满足匹配规则才会继续匹配之后的字符
 

其他实用技巧

命名分组

在()中采用"?groupName"的方式可以对这个分组进行命名。在字符串进行match方法匹配后返回的对象中有一个groups属性能直接获取到经过命名分组的字符串。

//应用举例
let str = '2021/11/14'
const regTest = /(?<year>\d{4})\/(?<month>\d{2})\/(?<date>\d{2})/;
let result = str.match(regTest);
console.log(result.groups) //{year: '2021', month: '11', date: '14'}

好啦,以上就是这期 JS正则表达式的总结内容,希望能对您有所帮助啦~~