说说正则那些事2

今天是六一儿童节啊,首先先祝福一下六一的小朋友们,节日快乐哦,不包含那些很大了还装小朋友的人儿哦,哈哈哈,开玩笑啦,心态年轻你们永远年轻啊,节日快乐节日快乐。(要不是怕挨打谁祝你们啊,切我堂堂…,真的很怂的啊),给大佬们递茶,大佬们永远年轻啊。言归正传,上篇我们讲述了正则表达式招式一招式二,那么今天我们就继续把招式三四都看了吧,说招式其实感觉有点不好,因为大圣老师说过高手过招拼的都是内功,算了毕竟咱是菜鸟。

  • 招式三:正则表达式括号的作用

    1. 分组和分支结构

      • 分组:比如我们要匹配连续的一个字符可以用/a+/,进行匹配,而要匹配连续出现的ab时那我们就要使用括号了,就需要使用/(ab)+/,其中括号这个时候就是作用于ab整个整体的

      • 分支结构:前边我们也见过了分支结构中就用了括号(P1|P2)中,此处的括号的作用也是不言而喻的,提供了子表达式的所有可能。

        var regex = /^I love (javascript|Regular Expression)$/g;
        console.log(regex.test("I love javascript"));
        console.log(regex.test("I love Regular Expression"));
        //true
        //true
        复制代码
    2. 捕获分组

      • 括号的另一个作用也就是进行数据提取啊,为了更好的进行替换操作

        var regex = /\d{4}-\d{2}-\d{2}/;
        // 可以修改成括号版的:
        var regex = /(\d{4})-(\d{2})-(\d{2})/;
        复制代码

        为什么用括号呢?那就来说说这么的好处的吧?,看下边的代码块返回的是一个数组,第一个元素就是返回整体匹配到的结果,然后就是各个分组(括号)匹配的内容,然后就是下标,最后是输出的文本,然后这样就可以很方便的提取数据了,并且使用构造函数的全局属性11-9来获取console.log(RegExp.$1); // “2020”

        var regex = /(\d{4})-(\d{2})-(\d{2})/;
        var string = "2020-06-01";
        console.log(string.match(regex));
        //["2020-06-01", "2020", "06", "01", index: 0, input: "2020-06-01", groups: undefined]
        复制代码
      • 毋庸置疑获取到了肯定就是替换了啊

        var regex = /(\d{4})-(\d{2})-(\d{2})/;
        var string = "2020-06-01";
        var result = string.replace(regex,function(){
          return RegExp.$2 + "/" + RegExp.$3 + "/" + RegExp.$1;
        });
        console.log(result);
        // 06/01/2020
        复制代码
    3. 反向引用

      • 大家肯定很疑惑什么是反向引用,咱们获取了这个正则匹配到的然后进行替换,这叫引用,然后我们获取到了这个匹配的东西,然后再拿过来在进行匹配这就叫反向引用,也是是获取捕获的内容然后引用。这样不好理解,咱还是直接上菜吧,翠花来上酸菜吧。

        // 要写个正则,需要匹配2020-06-01 2020/06/01 和2020.06.01,怎么进行写
        var regex = /\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/;
        var string1 = "2020-06-01";
        var string2 = "2020/06/01";
        var string3 = "2020.06.01";
        console.log(regex.test(string1));
        console.log(regex.test(string2));
        console.log(regex.test(string3));
        //true
        //true
        //true
        //==========停 这是一条正经的分割线 ,你有没有想过 "2020-06/01"这种形式也会匹配到呢?
        var string4 = "2020-06/01";
        console.log(regex.test(string4));
        //true  是的也会匹配到
        复制代码
      • 那怎么办?这个时候你肯定会在想要是前边匹配到啥后边也就匹配到啥就好了,可以!js这么强大怎么不会想你所想,反向引用来了

        var regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/;
        var string1 = "2020-06-01";
        var string2 = "2020/06/01";
        var string3 = "2020.06.01";
        var string4 = "2020-06/01";
        console.log(regex.test(string1));
        console.log(regex.test(string2));
        console.log(regex.test(string3));
        console.log(regex.test(string4));
        //true
        //true
        //true
        //false  这不就已经实现了
        复制代码
      • 括号嵌套怎么办???? 记住一个括号一个组,1就是最外边的,1就是最外边的,2下一层,$3再下一层(开始我也懵逼树下懵逼果了啊)

        var regex = /^((\d)(\d(\d)))\1\2\3\4$/;
        var string = "1231231233";
        console.log(RegExp.$1);  //123  第一层
        console.log(RegExp.$2); //1  // 第一个(/d)
        console.log(RegExp.$3)  // 23
        console.log(RegExp.$4)  // 3
        复制代码
      • \10 表示什么呢? 第十个分组?还是\1 和 0 呢?,显然是前者。

    4. 非捕获分组

      之前出现的分组,都会捕获它们匹配到的数据,以便后续引用,因此称他们为捕获型分组。

      如果只想要括号的最原始的特性,但不会进行引用不另作存储进行使用,那么可以使用非捕获型分组(?:p),再来看看一个栗子,

      var regex = /(?:ab)+/g;
      var string = "ababa abbb ababab";
      console.log(string.match(regex));
      // ["abab", "ab", "ababab"]
      复制代码
    5. 来几个栗子尝尝

      • 模拟字符串trim方法,去掉字符串的开头和结尾的空白符,两种方法

        function trim(str){
          return str.replace(/^\s+|\s+$/g,'');
        }
        console.log(trim(" foobar "));
        // foobar
        复制代码
        function trim(str){
          return str.replace(/^\s*(.*?)\s*$/g,"$1");   
          // 点 是任意字符,* 是取 0 至 无限长度,?是非贪婪模式
          // \s 代表空白符
          //.*?a 就是取前面任意长度的字符内,到底一个 a 出现,匹配如下容
        }
        console.log(trim(" foobar "));
        // foobar
        复制代码
      • 将每个单词的字母大写

        function titleize(str){
          return str.toLoewCase().replace(/?:^|\s\w/g,function(c){
            return c.toUpperCase();
          })
        }
        复制代码
      • 驼峰化

        function camelize(str){
          return str.replace(/[-_\s]+(.)?/g,function(match,c){
          	return c ? c.toUpperCase() :"";          
          })
        }
        console.log(camelize("-moz-transform"))
        复制代码
  • 招式四:正则表达式回溯法原理

    一看回溯这个词就肯定觉着比较高大上,本身这个程序员就是爱装13哈哈哈,确实这个词出现次数很多,那么大家理解不理解呢?

    • 没有回溯的匹配

      假设我们的正则是/ab{1,3}c/,其可视化形式是:

      而当目标字符串是”abbbc”时,就没有所谓的”回溯”。其匹配过程是:

      其中子表达式b{1,3}表示”b”字符连续出现1到3次。

    • 有回溯的匹配

      如果目标字符串是”abbc”,中间就有回溯

      图中的第五步出现了红颜色的区域,表示匹配不成功,此时已经匹配到了2个字符的”b”,准备尝试去匹配第三个时,结果发现第三个不是要匹配的”b”,是”c”,那么也就说明b{1,3}已经匹配完毕,状态就应该回到原来之前的匹配到2个”b”字符的状态,也就是回到第四步的状态,然后再去匹配c这个时候就匹配成功了。上边的第六步也就是回溯。

      再去举个栗子,这一天天的举这玩意真难啊

      字符串是”abbbc”,匹配过程就是

      其中的第七步和第十步就是回溯,第七步和第四步一样,此时都是b{1,3}匹配了两个b,而第十步与第三步一样,此时b{1,3}只匹配了一个”b”,这也是b{1,3}的最终匹配结果。

    • 常见的回溯形式

      回溯法简单来说就是:正因为有很多的可能,所以需要一个个进行尝试,直到到某一步时,整体匹配成功了,要么到最后都试完了发现后,发现整体匹配不成功,总之,万变不离一个字:试!试就完了,通俗的说就是我们常常说的笨方法,掰手指头哈哈哈哈,那么我门下边来看看正则表达式中,哪些地方会产生回溯呢?

      • 贪婪量词

        {1,3}这种就是贪婪量词的典范,因为贪啊,不管多少都一直吃一直吃,尝试着按照顺序往下吃,只要有可能就一直尝试尝试,实在吃不下再吐出来,咦好恶心啊哈哈哈哈,所以贼贪婪,但是能吃下就吃。这就是贪婪量词,但是这个时候有一个特殊的地方,当多个贪婪量词在一起并且有冲突的时候怎么办??那就符合社会法则了啊,先下手为强,后下手那就吃不到呗!机会总是留给有准备的人滴。

        var string = "12345";
        var regex = /(\d{1,3})(\d{1,3})/;
        console.log(string.match(regex));
        // ["12345", "123", "45", index: 0, input: "12345", groups: undefined]
        复制代码

        其中前边的/d{1,3}匹配到的是”123″,后边的/d{1,3}匹配到的事”45″.

      • 惰性量词

        咱们前边也说过,惰性量词就是在贪婪量词之后加个问号?,表示尽可能少的匹配,

        var string = "12345";
        var regex = /(\d{1,3}?)(\d{1,3})/;
        console.log(string.match(regex));
        // ["1234", "1", "234", index: 0, input: "12345", groups: undefined]
        复制代码

        其中\d{1,3}?只匹配到一个字符”1″,而后边的\d{1,3}匹配到了”234″。

        var string = "12345";
        var regex = /^(\d{1,3}?)(\d{1,3})$/;
        console.log(string.match(regex));
        复制代码

        虽然惰性量词不贪,但是也会有回溯现象。

        目标字符串”12345″,匹配过程是:

        虽然不贪,但是为了整体进行能够匹配成功,只能给你多塞一点了,因此最后\d{1,3}?匹配的字符是”12″,是两个数字,而不是一个。

      • 分支结构

        我们知道分支也是惰性的,比如/can|candy/,去匹配字符串”candy”,得到的结果是”can”,因为分支会一个一个尝试,如果前面的满足了,后面就不会再试验了。

        分支结构,可能前面的子模式会形成了局部匹配,如果接下来表达式整体不匹配时,仍会继续尝试剩下的分支。这种尝试也可以看成一种回溯。

        目标字符串是”candy”,匹配过程:

        上面第5步,虽然没有回到之前的状态,但仍然回到了分支结构,尝试下一种可能。所以,可以认为它是一种回溯的

    • 小结

      简单总结就是,正因为有多种可能,所以要一个一个试。直到,要么到某一步时,整体匹配成功了;要么最后都试完后,发现整体匹配不成功。

      1. 贪婪量词“试”的策略是:买衣服砍价。价钱太高了,便宜点,不行,再便宜点。
      2. 惰性量词“试”的策略是:卖东西加价。给少了,再多给点行不,还有点少啊,再给点。
      3. 分支结构“试”的策略是:货比三家。这家不行,换一家吧,还不行,再换。
  • 招式五:未完待续。。。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享