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
  • 相关阅读:
    hdu 1028 Ignatius and the Princess III (n的划分)
    CodeForces
    poj 3254 Corn Fields (状压DP入门)
    HYSBZ 1040 骑士 (基环外向树DP)
    PAT 1071 Speech Patterns (25)
    PAT 1077 Kuchiguse (20)
    PAT 1043 Is It a Binary Search Tree (25)
    PAT 1053 Path of Equal Weight (30)
    c++ 常用标准库
    常见数学问题
  • 原文地址:https://www.cnblogs.com/yalong/p/14133482.html
Copyright © 2011-2022 走看看