zoukankan      html  css  js  c++  java
  • js正则高级用法: 分组和断言

    原文连接: https://www.cnblogs.com/yalong/p/14133482.html

    分组概念的由来: 

      对于要重复单个字符,非常简单,直接在字符后加上限定符即可,例如 a+ 表示匹配1个或一个以上的a,a?表示匹配0个或1个a, 这些限定符如下所示:

    X ?

    X ,一次或一次也没有

    X *

    X ,零次或多次

    X +

    X ,一次或多次

    X { n }

    X ,恰好 n 次

    X { n ,}

    X ,至少 n 次

    X { n , m }

    X ,至少 n 次,但是不超过 m 次

    但是我们如果要对多个字符进行重复怎么办呢  此时我们就要用到分组,我们可以使用小括号"()"来指定要重复的子表达式,然后对这个子表达式进行重复,例如:(abc)? 表示0个或1个abc 这里一 个括号的表达式就表示一个分组 。

    分组可以分为两种形式,捕获组和非捕获组。

    捕获分组

    捕获性分组工作模式()会把每个分组里匹配的值保存起来, 保存在内存中。

    比如利用捕获性分组把 2010/11/12 转成 2010-11-12

    方法一:通过exec函数  

      let str = '2010/11/12
      let pattern = /(d+)/(d+)/(d+)/
      let arr = pattern.exec(str)
    
      console.log(arr); // ['2010/11/12', '2010', '11', '12']  
      console.log(arr[0]); //'2010/11/12' 匹配到的字符串
      console.log(arr[1]); //'2010' 第一个分组(d+)的值
      console.log(arr[2]); //'11'
      console.log(arr[3]); //'12'
    
      //这时候两个分组的值都得到了,接下来用字符串拼接法实现互换
      let result = `${arr[1]}-${arr[2]}-${arr[3]}`
      console.log(result) //2010-11-12

    方法二:通过属性$1-9

      let str = '2010/11/12'
      let pattern = /(d+)/(d+)/(d+)/
      pattern.test(str); //这个地方必须运行正则匹配一次,方式不限,可以是test()、exec()、以及String的正则方式
      console.log(RegExp.$1) //'2010' 第一个分组([a-z]+)的值
      console.log(RegExp.$2) //'11'
      console.log(RegExp.$3) //'12'
      let result = `${RegExp.$1}-${RegExp.$2}-${RegExp.$3}`
      console.log(result) //2010-11-12

    方法三:通过String的replace()

      let str = '2010/11/12'
      let pattern = /(d+)/(d+)/(d+)/
      let result = str.replace(pattern,"$1-$2-$3"); //这里的$1、$2与方法二里的RegExp.$1、RegExp.$2作用是相同的。
      console.log(result) //2010-11-12

    非捕获性分组:(?:)

    非捕获性分组工作模式下分组(?:)会作为匹配校验,并出现在匹配结果字符里面,但不作为子匹配返回

    非捕获组不会捕获文本,也不会将它匹配到的内容单独分组来放到内存中。所以,使用非捕获组较使用捕获组更节省内存

    比如利用非捕获性分组获取字符串000aaa111,而且只返回一个值为aaa111的数组:

      //先看用捕获性分组匹配会返回什么
      let str1 = '000aaa111';             
      let pattern = /([a-z]+)(d+)/; //捕获性分组匹配
      let arr = pattern.exec(str1);  
      console.log(arr) //['aaa111','aaa','111']   结果子串也获取到了,这并不是我们想要的结果
    
      //非捕获性分组
      let str2 = '000aaa111';
      let pattern2 = /(?:[a-z]+)(?:d+)/; //非捕获性分组匹配
      let arr2 = pattern2.exec(str2);  
      console.log(arr2) //['aaa111']  结果正确   

    断言分为先行断言(前瞻),后发断言(后顾)

    前瞻 = 先行断言
      (?=) 正向前瞻 = 正向零宽先行断言
      (?!) 反向前瞻 = 负向前瞻 = 负向零宽先行断言

    后顾 = 后发断言
      (?<=) 正向后顾 = 正向零宽后发断言
      (?<!) 反向后顾 = 负向后顾 = 负向零宽后发断言

    详细解释如下表所示:

    (?=X )

    零宽度正先行断言。仅当子表达式 X 在 此位置的右侧匹配时才继续匹配。例如,/w+(?=/d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。

    (?!X)

    零宽度负先行断言。仅当子表达式 X 不在 此位置的右侧匹配时才继续匹配。例如,例如,/w+(?!/d) 与后不跟数字的单词匹配,而不与该数字匹配 。

    (?<=X)

    零宽度正后发断言。仅当子表达式 X 在 此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后面的 99 的实例匹配。此构造不会回溯。

    (?<!X)

    零宽度负后发断言。仅当子表达式 X 不在此位置的左侧匹配时才继续匹配。例如,(?<!19)99 与不跟在 19 后面的 99 的实例匹配

    简单来说,前瞻就是 看后面等于(?=), 后面不等于 (?!),   后顾就是 前面等于 (?<=), 后面不等于 (?<!)

    比如   (?<!AA)eat(?=milk)   表示, eat 前面不能是AA, 后面必须是 milk 

    比如   (?<=AA)eat(?!milk)   表示   eat 前面必须是AA, 后面不能是milk

    示例1 匹配字符串:

      let str1 = "VVeatmilk"
      let str2 = "AAeatfood"
      let patt1 = new RegExp("(?<!AA)eat(?=milk)");
      let patt2 = new RegExp("(?<=AA)eat(?!milk)");
      let result1 = patt1.test(str1);
      let result2 = patt2.test(str2);
      console.log(result1) // true
      console.log(result2) // true

    示例2 匹配div标签:

      let str = "<div>我是div</div>"
      let patt = str.match('(?<=<div>).*(?=</div>)')
      console.log(patt) // 我是div

     匹配div标签 也可用 下面这种方式实现

      let str = "<div>我是div</div>"
      let patt = str.match('<div>(.*?)</div>')
      console.log(patt) // 我是div
  • 相关阅读:
    绕过验证码登陆的方法(适合只需登陆一次可以记住登陆台的网站)
    Throughput Controller(吞吐量控制器) 感觉就像个线程控制器来的
    时间戳 和 日期 转换的方法 (含获取当前时间戳的方法)
    pip使用笔记
    可以模拟多种浏览器的网站
    浏览器兼容性说明
    测试套件的使用
    python 时间对比大小 和 间隔多少天
    django数据库操作
    mysql连接工具记录
  • 原文地址:https://www.cnblogs.com/yalong/p/14133482.html
Copyright © 2011-2022 走看看