美文网首页
正则捕获-正则捕获的懒惰性,分组捕获

正则捕获-正则捕获的懒惰性,分组捕获

作者: 小柠有点萌 | 来源:发表于2020-07-01 10:33 被阅读0次

    let str = "hshshs2019sdfaa2020asdw2021";

    实现正则捕获的办法
    • 正则RegExp.prototype上的方法
      exec
      test
    • 字符串String.prototype上支持正则表达式处理的方法
      replace
      match
      splite

      基于exec实现正则的捕获
      捕获到的结果是null或者一个数组
      第一项:本次捕获到的内容
      其余项:对应小分组本次单独捕获的内容
      index:当前捕获内容在字符串中的起始索引
      input:原始字符串
      .每执行一次exec,只能捕获到一个符合正则规则的,但是默认情况下,我们执行一百遍,获取的结果永远都是第一个匹配到的,其余的捕获不到
      =>“正则捕获的懒惰性”:默认只捕获第一个
    let str = "zhufeng2019yangfan2020qihang2021";
    let reg = /\d+/;
    console.log(reg.exec(str)); //=>["2019", index: 7, input: "zhufeng2019yangfan2020qihang2021"]
    console.log(reg.exec(str)); //=>["2019"...]
    
    • 实现正则捕获的前提是:当前正则要和字符串匹配,如果不匹配捕获的结果是null
     let reg = /^\d+$/;
    console.log(reg.test(str)); //=>false
    console.log(reg.exec(str)); //=>null
    
    
    • 懒惰性的解决办法
      reg.lastIndex:当前正则下一次匹配的起始索引位置
      懒惰性捕获的原因:默认情况下lastIndex的值不会被修改,每一次都是从字符串开始位置查找,所以找到的永远只是第一个
      解决办法:全局修饰符g

    /*

    • reg.lastIndex:当前正则下一次匹配的起始索引位置
    • 懒惰性捕获的原因:默认情况下lastIndex的值不会被修改,每一次都是从字符串开始位置查找,所以找到的永远只是第一个
    • 解决办法:全局修饰符g
      */
    let reg = /\d+/;
            console.log(reg.lastIndex); //=>0 下面匹配捕获是从STR索引零的位置开始找
            console.log(reg.exec(str)); //["2019", index: 6, input: "hshshs2019sdfaa2020asdw2021", groups: undefined]
            console.log(reg.lastIndex); //=>0 第一次匹配捕获完成,lastIndex没有改变,所以下一次exec依然是从字符串最开始找,找到的永远是第一个匹配到的
    

    [全局捕获会动态修改匹配位置]

    let reg = /\d+/g;
            console.log(reg.exec(str)); //=>["2019"...]
            console.log(reg.lastIndex); //=>10 设置全局匹配修饰符g后,第一次匹配完,lastIndex会自己修改
            console.log(reg.exec(str)); //=>["2020"...]
            console.log(reg.lastIndex); //=>19
            console.log(reg.exec(str)); //=>["2021"...]
            console.log(reg.lastIndex); //=>27
            console.log(reg.exec(str)); //=>null 当全部捕获后,再次捕获的结果是null,但是lastIndex又回归了初始值零,再次捕获又从第一个开始了...
            console.log(reg.lastIndex); //=>0
            console.log(reg.exec(str)); //=>["2019"...]
    

    验证匹配捕获

    let reg = /\d+/g;
            if (reg.test(str)) {
                //=>验证一下:只有正则和字符串匹配我们在捕获
                console.log(reg.lastIndex); //=>10 基于TEST匹配验证后,LASTINDEX已经被修改为第一次匹配后的结果,所以下一次捕获不再从头开始了
                console.log(reg.exec(str)); //=>["2020"...]
            }
    

    demo编写把所有匹配的结果捕获到

    方法一

    // =>需求:编写一个方法execAll,执行一次可以把所有匹配数的结果捕获到(前提正则一定要设置全局修饰符g)
            ~ function () {
                function execAll(str = "") {
                    //=>str:要匹配的字符串
                    //=>this:RegExp的实例(当前操作的正则)
                    //=>进来后的第一件事,是验证当前正则是否设置了G,不设置则不能在进行循环捕获了,否则会导致死循环
                                  //global属性是指明是搜索整个字符串还是匹配第一个。整个是true,一个是false
                                  //let reg2 = /\d+/g;  //全局
                                  //dir(reg2 )
                                  //global =>true
                                  //let reg2 = /\d+/;  //一个
                                  //dir(reg2 )
                                  //global =>false
                    if (!this.global) return this.exec(str);
                    //=>ARY存储最后所有捕获的信息  RES存储每一次捕获的内容(数组)
                    let ary = [],
                        res = this.exec(str);
                    while (res) {
                        //=>把每一次捕获的内容RES[0]存放到数组中
                        ary.push(res[0]);
                        //=>只要捕获的内容不为NULL,则继续捕获下去
                        res = this.exec(str);
                    }
                    return ary.length === 0 ? null : ary;
                }
                RegExp.prototype.execAll = execAll;
            }();
    
            let reg = /\d+/g;
            console.log(reg.execAll("a'sa2019@2020asaffd"));  // =>["2019", "2020"]
            
    

    方法二match(不适用于分组捕获)

    //=>字符串中的MATCH方法,可以在执行一次的情况下,捕获到所有匹配的数据(前提:正则也得设置G才可以)
            console.log("ssr'r2019@2020sss".match(reg));  //=>["2019", "2020"]
    

    分组捕获

    正则的分组捕获
    //=>第一项:大正则匹配的结果
    //=>其余项:每一个小分组单独匹配捕获的结果
    //=>如果设置了分组(改变优先级),但是捕获的时候不需要单独捕获,可以基于?:来处理

    let str = "130828199012040112";
    let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/;
    console.log(reg.exec(str));
    console.log(str.match(reg));
    //=>["130828199012040112", "130828", "1990", "12", "04", "1", index: 0, input: "130828199012040112"]
    
    

    案例//=>{0}年{1}月{2}日,既要捕获到{数字},也想单独的把数字也获取到,例如:第一次找到 {0} 还需要单独获取0

    let str = "{0}年{1}月{2}日";
    //=>不设置g只匹配一次,exec和match获取的结果一致(既有大正则匹配的信息,也有小分组匹配的信息)
    let reg = /\{(\d+)\}/;
    console.log(reg.exec(str));
    console.log(str.match(reg));
    //["{0}", "0",...]
    */
    
    let reg = /\{(\d+)\}/g;
    //console.log(str.match(reg)); //=>["{0}", "{1}", "{2}"] 多次匹配的情况下,match只能把大正则匹配的内容获取到,小分组匹配的信息无法获取
    let aryBig=[],
        arySmall=[],
        res=reg.exec(str);
    while(res){
        let [big,small]=res;
        aryBig.push(big);
        arySmall.push(small);
        res=reg.exec(str);
    }
    console.log(aryBig,arySmall); //=>["{0}", "{1}", "{2}"] ["0", "1", "2"]
    

    =>分组的第三个作用:“分组引用”

    let str = "book"; //=>"good"、"look"、"moon"、"foot"...
    let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/; //=>分组引用就是通过“\数字”让其代表和对应分组出现一模一样的内容,oo,ee,让其代表和()里同样的内容
    console.log(reg.test("book")); //=>true
    console.log(reg.test("deep")); //=>true
    console.log(reg.test("some")); //=>false
    

    正则捕获的贪婪性

    let str = "ad2019@2020we";
    //=>正则捕获的贪婪性:默认情况下,正则捕获的时候,是按照当前正则所匹配的最长结果来获取的
    let reg = /\d+/g;
    console.log(str.match(reg)); //=>["2019","2020"]
    
    //=>在量词元字符后面设置?:取消捕获时候的贪婪性(按照正则匹配的最短结果来获取)
    reg = /\d+?/g;
    console.log(str.match(reg)); //=>["2", "0", "1", "9", "2", "0", "2", "0"]
    

    问号在正则中的五大作用:

    • 问号左边是非量词元字符:本身代表量词元字符,出现零到一次
    • 问号左边是量词元字符:取消捕获时候的贪婪性
    • (?:) 只匹配不捕获
    • (?=) 正向预查
    • (?!) 负向预查

    其它正则捕获的方法

    1. test也能捕获(本意是匹配)

      let str = "{0}年{1}月{2}日";
      let reg = /\{(\d+)\}/g;
      console.log(reg.test(str)); //=>true
      console.log(RegExp.$1); //=>"0"
      
      console.log(reg.test(str)); //=>true
      console.log(RegExp.$1); //=>"1"
      
      console.log(reg.test(str)); //=>true
      console.log(RegExp.$1); //=>"2"
      
      console.log(reg.test(str)); //=>false
      console.log(RegExp.$1); //=>"2" 存储的是上次捕获的结果
      
      //=>RegExp.$1~RegExp.$9:获取当前本次正则匹配后,第一个到第九个分组的信息
      
    2. replace 字符串中实现替换的方法(一般都是伴随正则一起使用的)

      let str = "sr@2019|sr@2020";
      //=>把"sr"替换成"shao"
      //1.不用正则,执行一次只能替换一个
      /*
      str = str.replace("sr","shao").replace("sr","shao");
      console.log(str);
      */
      //2.使用正则会简单一点
      str = str.replace(/sr/g,"shao");
      console.log(str);
      
      let str = "sr@2019|sr@2020";
      //=>把"sr"替换为"sr111"
      //str=str.replace("sr","sr111").replace("sr","sr111");
      //"sr111@2019|sr@2020" 每一次替换都是从字符串第一个位置开始找的(类似于正则捕获的懒惰性)
      
      //=>基于正则g可以实现
      str = str.replace(/sr/g,"sr111");
      

      案例:把时间字符串进行处理

      let time = "2019-08-13";
      //=>变为"2019年08月13日"
      let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
      
      //=>这样可以实现
      //time = time.replace(reg,"$1年$2月$3日");
      //console.log(time); //=>2019年08月13日
      
      //=>还可以这样处理 [str].replace([reg],[function])
      //1.首先拿REG和TIME进行匹配捕获,能匹配到几次就会把传递的函数执行几次(而且是匹配一次就执行一次)
      //2.不仅把方法执行,而且REPLACE还给方法传递了实参信息(和exec捕获的内容一致的信息:大正则匹配的内容,小分组匹配的信息....)
      //3.在函数中我们返回的是啥,就把当前大正则匹配的内容替换成啥
      /*
      time = time.replace(reg,(big,$1,$2,$3)=>{
          //=>这里的$1~$3是我们自己设置的变量
          console.log(big,$1,$2,$3);
      });
      */
      time = time.replace(reg,(...arg)=>{
          let [,$1,$2,$3]=arg;
          $2.length<2?$2="0"+$2:null;
          $3.length<2?$3="0"+$3:null;
          return $1+"年"+$2+"月"+$3+"日";
      });
      

      单词首字母大写

      let str = "good good study,day day up!";
      let reg = /\b([a-zA-Z])[a-zA-Z]*\b/g;
      //=>函数被执行了六次,每一次都把正则匹配信息传递给函数
      //=>每一次ARG:["good","g"] ["good","g"] ["study","s"]...
      str = str.replace(reg,(...arg)=>{
          let [content,$1]=arg;
          $1=$1.toUpperCase();
          content=content.substring(1);
          return $1+content;
      });
      console.log(str); //=>"Good Good Study,Day Day Up!"
      

      验证一个字符串中那个字母出现的次数最多,多少次?

      /*==(去重思维)==*/
      let str = "dfsfdgfhrtvjoemk";
      let obj = {};
      [].forEach.call(str, char => {
       if (typeof obj[char] !== "undefined") {
           obj[char]++;
           return;
       }
       obj[char] = 1;
      });
      let max = 1,
       res = [];
      for (let key in obj) {
       let item = obj[key];
       item > max ? max = item : null;
      }
      for (let key in obj) {
       let item = obj[key];
       if (item === max) {
           res.push(key);
       }
      }
      console.log(`出现次数最多的字符:${res},出现了${max}次`);
      
      /*==:排序==*/
      let str = "qwefgskoilxsg";
      str = str.split('').sort((a, b) => a.localeCompare(b)).join('');
      // console.log(str);//=>"aeefghhhiilnnoopsuuuxzz"
      let ary = str.match(/([a-zA-Z])\1+/g).sort((a, b) => b.length - a.length);
      // console.log(ary); //=>["hhh", "uuu", "ee", "ii", "nn", "oo", "zz"]
      let max = ary[0].length,
       res = [ary[0].substr(0, 1)];
      for (let i = 1; i < ary.length; i++) {
       let item = ary[i];
       if (item.length < max) {
           break;
       }
       res.push(item.substr(0, 1));
      }
      console.log(`出现次数最多的字符:${res},出现了${max}次`);
      
      /*==:从最大到最小去试找==*/
      let str = "qwefgskoilxsg",
       max = 0,
       res = [],
       flag = false;
      str = str.split('').sort((a, b) => a.localeCompare(b)).join('');
      for (let i = str.length; i > 0; i--) {
       let reg = new RegExp("([a-zA-Z])\\1{" + (i - 1) + "}", "g");
       str.replace(reg, (content, $1) => {
           res.push($1);
           max = i;
           flag = true;
       });
       if (flag) break;
      }
      console.log(`出现次数最多的字符:${res},出现了${max}次`);
      

      其它方法:formatTime 、 queryURLParams 、 millimeter

      ~ function () {
       /*
        * formatTime:时间字符串的格式化处理
        *   @params
        *     templete:[string] 我们最后期望获取日期格式的模板
        *     模板规则:{0}->年  {1~5}->月日时分秒
        *   @return
        *     [string]格式化后的时间字符串
        *  by sr on 2019/08/13
        */
       function formatTime(templete = "{0}年{1}月{2}日 {3}时{4}分{5}秒") {
           let timeAry = this.match(/\d+/g);
           return templete.replace(/\{(\d+)\}/g, (...[, $1]) => {
               let time = timeAry[$1] || "00";
               return time.length < 2 ? "0" + time : time;
           });
       }
      
       /* 
        * queryURLParams:获取URL地址问号和面的参数信息(可能也包含HASH值)
        *   @params
        *   @return
        *     [object]把所有问号参数信息以键值对的方式存储起来并且返回
        * by sr on 2019/08/13
        */
       function queryURLParams() {
           let obj = {};
           this.replace(/([^?=&#]+)=([^?=&#]+)/g, (...[, $1, $2]) => obj[$1] = $2);
           this.replace(/#([^?=&#]+)/g, (...[, $1]) => obj['HASH'] = $1);
           return obj;
       }
      
       /* 
        * millimeter:实现大数字的千分符处理
        *   @params
        *   @return
        *     [string]千分符后的字符串
        * by sr on 2019/08/13
        */
       function millimeter() {
           return this.replace(/\d{1,3}(?=(\d{3})+$)/g, content => content + ',');
       }
       
       /* 扩展到内置类String.prototype上 */
       ["formatTime", "queryURLParams", "millimeter"].forEach(item => {
           String.prototype[item] = eval(item);
       });
      }();
      

    相关文章

      网友评论

          本文标题:正则捕获-正则捕获的懒惰性,分组捕获

          本文链接:https://www.haomeiwen.com/subject/tbanfktx.html