【JS】正则表达式,正则匹配,正则捕获

1,251 阅读5分钟

一些正则的小案例

手机号的正则

 /* 
   手机号
      1、11位数字
      2、1开头
      3、第二位是3-9
 */
        let reg = /^1[3-9]\d{9}$/
        let num = '15031418888';
        console.log(reg.test(num));//true

有效数字的正则

  /* 
      有效数字
      2 +2 -2  0  2.5   -21.5    3.01415926   3.1
      1、开头可以是 +或者- (可有可无,如果有只能出现一次) [+-]?   出现0到1次
      2、数字有可能是1位数,也有可能是多位数   (\d|[1-9]\d+)  
      3、小数部分     (\.\d+)?
   */
         //  test:他是正则类原型上的一个方法
         //注意有效数字不能是0开头的多位数字,这个地方很容易出错
         
     let reg = /^[+-]?(\d|[1-9]\d+)(\.\d+)?$/;

     console.log(reg.test('23.1')); // true
     console.log(reg.test('23.1.1')); // false
     console.log(reg.test('0')); // true
     console.log(reg.test('023')); // false

   

密码的正则

   /* 
    密码:
        1、6-18位组成
        2、由数字,字母,下划线组成
        let reg = /^\w{6,18}$/
    */
       let str = 'rtyu567&';
  function fn(str){
        if(str.length<6 || str.length>18){
           alert('密码不符合规则');
           return;
        }
      let ary =['r','1','a','_']; //这里可以规定一些必须包含的字符
      for(var i = 0;i<str.length;i++){
        if(!ary.includes(str[i])){ 
            alert('密码不符合规则');
            return;
        }
      }
      let reg = /^\w{6,18}$/;
      //如果字符串是null或undefined,提示不符合规则
      if(!reg.test(str)){
        alert('密码不符合规则');
        return;
           }
  }
        fn(str);

匹配姓名的正则

      /* 
      匹配姓名
        宋小宝
        宋尊
        东方不败
        喜塔腊·尔晴
        上杉绘梨衣
        爱新觉罗·溥仪
        希林那依·高
        阿诺德·施瓦辛格·溥仪
        尼古拉斯·赵四
        [\u4E00-\u9FA5]
       */
      let reg = /[\u4E00-\u9FA5]{2,6}(·[\u4E00-\u9FA5]{1,6}){0,10}/;
    //点后面的部分可有可无,也可以出现多次,因此也可以用一个星号,表示零到多次。此处规定了0到10次。
    //点后面的每部分的长度也规定了1到6个字 

简单身份证的正则(不完整)

/* 
    身份证
      1、18位
      3、前六位:省市区
      4、7到14位:出生年月日
      5、最后四位:
          1、前两位:带出所的代码
          2、第三位:奇数是男孩,偶数是女孩
          3、第四位:有可能是数字,也有可能是X
 */
      //写正则之前先分析各种情况,注意分好组再写
      //  年  [1-2]\d{3}
      //  月   01-12   01-09  10-12  (0[1-9]|1[0-2])
      //  日   01-31   01-09 10-29  30-31  (0[1-9]|[1-2][0-9]|3[0-1])
        
      // ([0 - 2][1 - 9]) | 10 | 20 | 30 | 31)
              
      // ?: 表示只匹配不捕获        
              
       let reg = /^\d{17}(\d|X)$/;
           reg = /^(\d{6})(\d{8})(\d{2})(\d{1})(\d|X)$/;
           reg = /^(?:\d{6})([1-2]\d{3})(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(?:\d{2})(\d{1})(?:\d|X)$/;
------------------------------------------------------------------
       let str = '11011100010318238X';
       function fn(str){
         let reg = /^(?:\d{6})([1-2]\d{3})(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(?:\d{2})(\d{1})(?:\d|X)$/;
         let res = reg.exec(str);
         // 如果捕获不到是null
         if(!res){
           console.log('对不起,你的身份证号有误,请重新输入');
           return;
         }
         // ["11011120010318237X", "2001", "03", "18", "7", index: 0, input: "11011120010318237X", groups: undefined]
         let sex = res[4] %2 === 0? '女孩' :'男孩';
         return `你的生日是${res[1]}年${res[2]}月${res[3]}日,你的性别是${sex}`
       }
       console.log(fn(str)); // xxxx年xx月xx日 你的性别是x

      //  ()
      //  1、提高匹配的优先级
      // 2、分组引用
      // 3、分组捕获
      // ?:只匹配不捕获

        console.log(reg.test(str));
        console.log(reg.exec(str));

正则捕获

正则类原型上的方法

  • test: 匹配
  • exec:捕获

字符串原型上的方法(这三个方法都可以实现捕获)

  • replace
  • match
  • matchAll 在调用方法的时候要区分清楚,是哪个类的原型上面的方法

正则的捕获与懒惰性

exec

1、返回值是一个数组(如果捕获不到内容就是null)

  1. 第一项是当前捕获的内容

  2. index是当前捕获到的内容位置的索引

  3. input:源字符串

2、exec默认只能第一次符合规则的内容(这是正则的懒惰型,但是修饰符g可以消除正则的懒惰型)

    let str = 'qw123ert456yuio789p';
    let reg = /\d{3}/g;
// 如果正则不加g,那每一次去捕获,只能捕获到第一次符合规则的内容,当前正则身上的lastIndex都是0,(lastIndex是每一次正则匹配开始的位置)
       
// 不加g的情况
       console.dir(reg.lastIndex); // 0
       console.log(reg.exec(str)); // ['123']
       console.dir(reg.lastIndex); // 0
       console.log(reg.exec(str)); // ['123']
       console.dir(reg.lastIndex); // 0
       console.log(reg.exec(str)); // ['123']
       // ["123", index: 2, input: "qw123ert456yuio789p", groups: undefined]
-------------------------------------------------------------------------------
// 加g的情况
       console.log(reg.lastIndex); // 0
       console.log(reg.exec(str));  // ['123']
       console.log(reg.lastIndex); // 5
       console.log(reg.exec(str));  // ['456']
       console.log(reg.lastIndex); // 11
       console.log(reg.exec(str));  // ['789']
       console.log(reg.lastIndex); // 18
       console.log(reg.exec(str));  // null
       console.log(reg.lastIndex); // 0
       console.log(reg.exec(str));  // ['123']  跟第一次一样了,就实现循环了
// 如果正则加上g,那每捕获一次,正则的lastIndex就会记录当前捕获到的内容的最后一项索引的值,当下一次再继续捕获的时候,会从上一次记录的索引的基础上加1进行捕获,
//  直到捕获到null为止,在继续从头进行捕获
       reg.lastIndex = 5; // 如果正则加上g,就可手动改变lastIndex的值,从而改变捕获的位置
       console.log(reg.exec(str));
// 由于正则没有直接获取所有符合规则的内容的方法,咱们要自己封装一个类似的方法
      let str = 'qw123ert456yuio789p';
       let reg = /\d{4}/g;
      //  console.log(reg.test(str));
      // console.dir(reg)

      function myExec(str){
        // 如果当前正则没有加修饰符g,把正则身上的global属性的值是false,如果加上了,那就是true
        if(!this.global){
          // 把第一次捕获到的内容return出去就行了,
          return this.exec(str); // 就是为了和原生的exec的返回值保持一致,并且阻止代码向下运行
        };
        let ary = []; // 创建一个数组,用来存储每一次捕获到的内容
        let res = this.exec(str); // ['123']
        while(res){
            ary.push(res[0]); // 把捕获到的内容push到ary数组中
            res = this.exec(str); // 继续捕获,把捕获到的新内容在赋值给res,直到捕获到null为止,就停止循环(因为捕获到null说明已经捕获到底了)
        };
        return ary.length === 0? null:ary;
        // 如果ary的length是0,说明当前正则一次也没有捕获到,那这时候应该return null
      };
      RegExp.prototype.myExec = myExec;
      console.log(reg.myExec(str)); // ['123','456','789']
      console.log(str.match(reg));

分组捕获

正则的分组捕获

    // ==>身份证号
     let reg = /^(?:\d{6})(\d{8})(?:\d{2})(\d{1})(?:\d|X)$/;
     let str = '11011020030318238X'
     console.log(reg.exec(str));

exec

1、返回值是一个数组(如果捕获不到内容就是null)

1)第一项是大正则捕获的内容

2)其余项:每一个小分组捕获的结果

3)index是当前捕获到的内容位置的索引

4)input:源字符串

2、exec默认只能第一次符合规则的内容(这是正则的懒惰型,但是修饰符g可以消除正则的懒惰型)

3、如果设置了分组,但是只是想改变匹配的优先级,并不想捕获,可以给当前分组的开头加上 ?:来设置只匹配不捕获

    // let str = '{0}年{1}月{2}日';
    // let reg = /\{(\d)\}/g;
    // console.log(reg.exec(str)); // ['{0}','0']
    // console.log(reg.exec(str)); // ['{1}','1']
    // console.log(reg.exec(str)); // ['{2}','2']

    // console.log(str.match(reg)); // ["{0}", "{1}", "{2}"]
    // 如果正则加上g那match只能捕获到每一次大正则匹配的内容,无法获取到每一次小分组的匹配信息
    // 如果正则不加g,那match和exec捕获的结果是一致的
--------------------------------------------------
    // console.log(str.matchAll(reg)); // matchAll方法的返回值是一个遍历器,去配合for of去使用可以得到每一次捕获的内容
    // let bigAry = [];
    // let smallAry = [];

    // 当前循环会输出每一次捕获的结果
    // for(var ss of str.matchAll(reg)){
    //   let [$1,$2] = ss; // 代表每一次捕获到的内容
    //   bigAry.push($1);
    //   smallAry.push($2);
    // };
    // console.log(bigAry,smallAry);

注意: match方法无法进行分组捕获,只能捕获大正则的,小分组里面的捕获不到,分组捕获只能用exec 或者 matchAll,缺点就是大正则和小分组的正则捕获到的内容放在了同一个数组里面,没有分开。

注意: matchAll方法,如果正则不加g,不是全局捕获,会提示错误的,必须加g的正则才可以使用matchAll方法

matchAll方法全局捕获的过程

注意:小分组是在大正则的里面的。

捕获到的第一项是符合大正则的内容的第一部分,在这部分里面 符合小分组的内容,找到了以后放在数组的第二项及以后

然后再捕获符合大正则的第二部分,再从这部分里面找到符合小分组的放在后面

......

如此这样,一直捕获到最后一个符合大正则的部分,把这部分里面符合小分组的放在后面,就结束了

    // 封装一个方法,用来实现得到当前每一次捕获的大内容和分组捕获的小内容

//加入了判断,判断正则表达式中是否加了g(全局捕获)
//其实也可以不用加这个判断,因为matchAll方法使用时如果正则不加g,会报错
//不过为了让不加g也可以有输出,加入了这个判断

  function myMatch(reg) {
      if (!reg.global) {
        return reg.exec(this);
      }
      let bigAry = [];
      let smallAry = [];

      let res = this.matchAll(reg);
      for (var ss of res) {
        let [$1, $2] = ss;
        bigAry.push($1);
        smallAry.push($2);
      }

      let obj = {
        bigAry,
        smallAry
      }
      return bigAry.length === 0 ? null : obj;

    }
    String.prototype.myMatch = myMatch;
 

    let str = '{0}年{1}月{2}日';
    let reg = /\{(\d)\}/g;
    console.log(str.myMatch(reg));
    // {
    //   bigAry:['{0}','{1}','{2}'],
    //   smallAry:['0','1','2']
    // }

正则的贪婪性

   let str = 'wer202tyui2018op';
   let reg = /\d+?/g;
   console.log(reg.test(str));// true
  
 //正则的贪婪性==>默认请情况下,正则捕获的时候是按照当前正则所匹配的最长的字符来捕获的
   console.log(str.match(reg));
  //  ["202", "2018"] 这是带有贪婪性的
  // ["2", "0", "2", "2", "0", "1", "8"] 这是取消贪婪性的
   // ==>在量词元字符后面加?就可以取消捕获时的贪婪性(按照匹配的最短的字符来获取)

  //-----------------------------------------------------------------------------------
  // 利用正则的匹配test实现捕获
   let str = '{0}年{1}月{2}日';
   let reg = /\{(\d)\}/g;

   // 利用test匹配的时候,每匹配一次就会给正则类的$1~$9增加当前分组捕获的内容
  // $&:是每一次捕获的大内容
    console.log(reg.test(str));
    console.dir(RegExp)
    console.log(RegExp.$1); // '0'
    console.log(reg.test(str));
    console.dir(RegExp)
    console.log(RegExp.$1); // '1'
    console.log(reg.test(str));
    console.log(RegExp.$1); // '2'