美文网首页
正则表达式学习 - 捕获 - 分组捕获

正则表达式学习 - 捕获 - 分组捕获

作者: 岁月静好_不负此生 | 来源:发表于2019-11-17 14:05 被阅读0次

    1 实现正则捕获的方法

    • 正则RegExp.prototype方法
      • exec: 一个在字符串中执行查找匹配的RegExp方法,它返回一个数组(未匹配到则返回 null)。
      • test: 一个在字符串中测试是否匹配的RegExp方法,它返回 true 或 false。
    • 字符串String.prototype方法
      • replace: 一个在字符串中执行查找匹配的String方法,并且使用替换字符串替换掉匹配到的子字符串。
      • match: 一个在字符串中执行查找匹配的String方法,它返回一个数组,在未匹配到时会返回 null。
      • matchAll: 一个在字符串中执行查找所有匹配的String方法,它返回一个迭代器(iterator)。
      • split: 一个使用正则表达式或者一个固定字符串分隔一个字符串,并将分隔后的子字符串存储到数组中的 String 方法。
      • search: 一个在字符串中测试匹配的String方法,它返回匹配到的位置索引,或者在失败时返回-1。

    2. 方法解析

    2.1 exec方法

    正则捕获的懒惰性: 默认只捕获第一个

    // 例
    let str = 'sdfsadf12354asdfas412456aasdf';
    let reg = /\d+/; // 捕获的正则表达式需要能过验证通过捕获对象字符串的
    console.log(reg.exec(str)); // => ["12354", index: 7, input: "sdfsadf12354asdfas412456aasdf", groups: undefined]
    
    /*
        * 结果: 
        * 1. 捕获到的结果是null(正则验证false) 或者一个数组
        *    第一项: 本次捕获到的内容
        *    其余项: 对应小分组本次单独捕获的内容
        *    index: 当前捕获内容在字符串中的起始索引
        *    input: 原始字符串
        * 2. 每执行一次exec, 只能捕获到一个符合正则规则的 => 称之为'正则捕获的懒惰性': 默认只捕获第一个
    */
    

    **懒惰性捕获的原因: **

    • 懒惰性捕获的原因: 默认情况下lastIndex的值不会被修改, 每一次都是从字符串开始位置查找, 所以找到的元素永远只是第一个

    解决方法:

    • 设置全局匹配修饰符g后, 会自动修正lastIndex的值(手动修改是无用的)
    // 例
    let str = 'sdfsadf12354asdfas412456aasdf';
    let reg = /\d+/g; // 捕获的正则表达式需要能过验证通过捕获对象字符串的
    /*
        * reg.lastIndex: 当前正则下一次匹配的起始索引位置
        *  懒惰性捕获的原因: 默认情况下lastIndex的值不会被修改, 每一次都是从字符串开始位置查找, 所以找到的元素永远只是第一个
        * 直接修改lastIndex的值是无效的
        * 通过设置全局匹配修饰符g, 会自动修正lastIndex的值
    */
    console.log(reg.exec(str)); // => ["12354", index: 7, input: "sdfsadf12354asdfas412456aasdf", groups: undefined]
    console.log(reg.exec(str));// => ["412456", index: 18, input: "sdfsadf12354asdfas412456aasdf", groups: undefined]
    console.log(reg.exec(str));// => null 当全部捕获后, 再次捕获的结果是null, 但是lastIndex又回归了初始值0, 再次捕获时又会从头开始 
    

    2.2 test方法

    test方法也会捕获, 但是不会返回捕获结果

    • 若是设置了全局修复符g, 使用test方法时会将lastIndex调整为下一个捕获的lastIndex

      // 例
      let str = 'sdfsadf12354asdfas412456aasdf';
      let reg = /\d+/g; // 捕获的正则表达式需要能过验证通过捕获对象字符串的
      // 用同一个正则先去验证字符串的话, 这样捕获会出现问题, 此时可以在用同一个正则规则去验证, 不要使用同一个正则对象
      if (reg.test(str)) {
          console.log(reg.lastIndex); // => 12 
          console.log(reg.exec(str)); // => ["412456", index: 18, input: "sdfsadf12354asdfas412456aasdf", groups: undefined]
      }
      

    2.3 String.prototype.match方法: 捕获所有匹配组

    这是字符串原型上的方法, 捕获所有匹配项

    // 没有加修饰符g的时候, 直接返回第一匹配项
    let reg = /\d+/;
    let str = 'asf123asd124adf154adf';
    str.match(reg) // => ["123", index: 3, input: "asf123asd124adf154adf", groups: undefined]
    
    // ======================
    // 返回所有匹配项
    let reg = /\d+/g;
    let str = 'asf123asd124adf154adf';
    str.match(reg) // =>  ["123", "124", "154"]
    
    // =======================
    // 没有匹配项时, 返回null
    let reg = /\d+/g;
    let str = 'dfasdfsd';
    str.match(reg) // => null 
    

    2.4 String.prototype.replace: 字符串替换方法

    • 一般伴随正则一起使用 -- 正则可以全局替换

      // 将suiyue替换成岁月
      let str = 'suiyue@2019|suiyue@2020';
      /*
        * 不使用正则 => str.replace('suiyue', '岁月') => 一次只能替换一个
        * 使用正则 => str.replace(/suiyue/g, '岁月')
      */
      
      // ======================
      // 例子: 处理时间字符串
      let time = '2019-08-13';
      // => 处理为2019年08月13日
      let reg = /^(\d{4})-(\d{1,2})-(\d${1,2})$/;
      
      // 实现方法一: $1为分组捕获的第一个, $2为分组捕获的第二个... 
      time.replace(reg, '$1年$2月$3日');
      
      // 实现方法二: 
      time.replace(reg, (big, ...arg) => {
          // arg为剩余参数, 值为: $1-$9
          // $1为分组捕获的第一个, $2为分组捕获的第二个...  
      })
      
      // ================
      // 例子: 单词首字母大写
      let str = 'good good study. day day asdf';
      let reg = /\b([a-zA-Z])[a-zA-Z]*\b/g;
      // 函数被执行了六次(相当于执行一次捕获一个), 每一次都把正则匹配信息传递给函数
      // => 每一次参数: ['good', 'g'] ['good', 'g'] ['study', 's']...
      str = str.replace(reg, (...arg) => {
          let [content, $1] = arg;
          $1 = $1.toUpperCase();
          conetnt = content.substring(1);
          return $1 + content;
      })
      console.log(str); // => Ggood Ggood Sstudy. Dday Dday Aasdf
      

    3. 正则的分组捕获

    • 一次匹配下, 分组捕获

    let str = '360145199011220112';
    /*
    小括号作用;
    1. 改变优先级
    2. 分组捕获 => 会将捕获的内容进行分组捕获
    */
    let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|x|X)/; console.log(reg.exec(str)); // => ["360145199011220112", "360145", "1990", "11", "22", "1", "2", index: 0, input: "360145199011220112", groups: undefined] // => 第一项: 大正则匹配的结果 // => 其余项: 每一个小分组单独匹配捕获的结果 // => 如果设置了分组, 只是为了改变优先级, 不需要分组捕获, 可以设置?:元字符 // 例如: /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|x|X)/

    
    * 多次匹配下, match只能把大正则匹配的内容获取到, 小分组(小括号分组)匹配的信息无法获取
    
    ```javascript
    let str = '{0}年{1}月{2}日';
    let reg = /\{(\d+)\}/g;
    // 使用match分组捕获时, 多次匹配无法捕获组
    console.log(str.match(reg)); // => ["{0}", "{1}", "{2}"]
    
    • 分组的另一个作用: '分组引用'

      let str = 'book'; // 假设需要保证中间两个字母相同
      let reg = /^[a-zA-Z([a-zA-Z])\1[a-zA-Z]]$/; // =>  分组引用就是通过'\数字'让其代表和对应分组出现一模一样的内容
      console.log(reg.test('book)); // => true
      console.log(reg.test('deep)); // => true
      console.log(reg.test('some)); // => false
      

    4. 正则捕获的贪婪性

    • 默认情况下, 正则捕获的时候, 是按照当前正则所匹配的最长结果来获取的

      let str = 'sdf230asdf123';
      let reg = /\d+/g;
      // 2 和 3 和 0 ..应该都符合正则reg的, 但是因为正则捕获的贪婪性, 会按照最长结果来匹配
      console.log(str.match(reg)); // => ['230', '123'];
      
      // ===================== 
      // 在 量词元字符 后面设置? : 取消捕获时候的贪婪性(按照正则匹配的最短结果来获取)
      reg = /\d+?/g;
      console.log(str.match(reg)) // =>  ["2", "3", "0", "1", "2", "3"]
      
    • 问号(?)在正则中的作用:

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

    5. 参考资料

    B站-Web前端快速入门正则表达式2019最新正则教程

    相关文章

      网友评论

          本文标题:正则表达式学习 - 捕获 - 分组捕获

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