美文网首页
正则表达式匹配规则

正则表达式匹配规则

作者: zz632893783 | 来源:发表于2019-10-07 12:30 被阅读0次

    本文介绍的并不是正则表达式的通配符含义,或者一些正则的书写技巧
    而是介绍正则匹配的流程,正则进行匹配的规则

    1.首先字符串中的每一个字符都有位置

    例如字符串:abcde
    这个时候位置关系如下

         a      b      c      d      e
      ↑      ↑      ↑      ↑      ↑      ↑
     0位置  1位置  2位置   3位置  4位置  5位置
    

    2.正则表达式有一个占位的概念

    分为两种
    ①是普通匹配,普通匹配占宽度,匹配的是字符
    ②是断言,断言不占宽度,匹配的是某一个位置,所以断言也被叫做零宽度断言。

    举个例子

    例如表达式:/\w(?=\d)/
    首先表达式拆解为 \w 与 (?=\d) 两个部分,
    表达式 \w 匹配一个字符,占用一个宽度,(?=\d) 表示一个数字的位置
    整个正则表达式连接起来就是,首先匹配一个字母或者数字 \w,匹配的这个 \w 后面必须紧跟一个数字 (?=\d)
    这里的 \w 为普通匹配,占用宽度,而 (?=\d) 为断言,并不占用宽度,只是规定 \w 后面必须含有一个数字

    看一下使用上面这个表达书进行匹配的结果

    ('abc123').match(/\w(?=\d)/);
    // 匹配到的结果为 ['c', index: 2, input: 'abc123']
    

    断言由于不占宽度所以match的结果只有字符'c'

    3.正则表达式的控制权概念

    联系第 1 点和第 2 点
    例如我们有一个正则表达式,和一个需要进行匹配的字符串

    let reg = /bc/g;
    let str = 'abcdabc';
    

    首先对字符串位置做标记,方便我们观察

          a      b      c      d      a      b      c
      ↑      ↑      ↑      ↑      ↑      ↑      ↑      ↑
    0位置   1位置  2位置   3位置  4位置  5位置   6位置  7位置
    

    同样的,我们对正则表达式 /bc/g 进行拆解为 b 与 c 两部分

    匹配的过程如下:

    1. 整个正则表达式的起始匹配字符为 b,整个正则控制权在 b 手中,首先从 0 位置开始匹配字符串 abcdabc
    2. 0 位置之后字符为 a,匹配失败,匹配位置由 0 位置转移到 1 位置,正则控制权回到整个正则表达式的最前面,所以这个时候控制权依旧在 b 手中(其实这里还牵扯到回溯,这里暂时先不做讨论)
    3. 匹配位置转移到 1 之后,从 1 位置开始匹配,字符串 1 位置之后的字符为 b,此时正则的控制权也是在 b 字符上,匹配成功,匹配的位置从 1 变为 2 ,由于正则表达式中的 b 字符没有做量词修饰,即它表示只占用 1 个位置的宽度,所以 b 的控制结束,控制权移交给正则的下一个匹配字符 c
    4. 从 2 位置继续匹配,字符串 2 位置之后的字符为 c,此时正则的控制权也正好在字符 c 上,匹配成功,匹配位置从 2 变为 3,由于 c 没有做任何量词的修饰,表示他只占用 1 个宽度,所以 c 的控制结束,控制权继续移交给正则的下一个匹配字符
    5. 匹配字符 c 之后已经没有其他的匹配字符了,整个正则表达式结束,此时字符串的匹配位置依旧是上一步结束时候的 3 位置
    6. 由于正则表达式声明为 g 全局匹配,所以整个正则表达式将从头开始,控制权移交给整个正则表达式的起始字符 b
    7. 正则表达式控制权在 b,匹配位置为之前匹配成功的结束位置 3,字符串 3 位置之后的字符为 d,表达式 b 匹配字符 d 失败,控制权依旧在起始位置 b,字符串匹配位置由 3 变为 4
    8. 正则表达式控制权在 b,匹配位置为 4,字符串 4 位置时候的字符为 a,表达式 b 匹配字符 a 失败,控制权依旧在起始位置 b,字符串匹配位置由 4 变为 5
    9. 正则表达式控制权在 b,匹配位置为 5,字符串 5 位置时候的字符为 b,匹配成功,匹配的位置从 5 变为 6 位置,由于正则表达式中的 b 字符没有做量词修饰,即它表示只占用 1 个位置的宽度,所以 b 的控制结束,控制权移交给正则的下一个匹配字符c
    10. 正则表达式控制权在 c,匹配位置为 6,字符串 6 位置时候的字符为 c,匹配成功,匹配的位置从 6 变为 7 位置,由于正则表达式中的 c 字符没有做量词修饰,即它表示只占用 1 个位置的宽度,所以 c 的控制结束,控制权继续向后移交
    11. 正则表达式结束,由于它是全局匹配,控制权再次回到 b,但是由于这个时候所匹配的字符串已经结束了,所以整个匹配过程结束

    4.回溯

    ①当正则匹配不成功的时候,就会尝试进行回溯
    ②回溯成功与否取决于是否有可回溯的位置
    ③若没有会回溯位置,则整个正则表达式匹配失败,控制权交还给表达式的起始位置
    ④正则规则中使用量词修饰,或者使用|的时候,所匹配的位置为可回溯位置

    依旧是一个简单的例子

        let reg = /ab{1,3}bbc/;
        let str = 'abbbc';
    
          a      b      b      b      c
      ↑      ↑      ↑      ↑      ↑      ↑
    0位置   1位置  2位置   3位置  4位置  5位置
    

    依旧对正则进行拆解,拆解成 a,b{1,3},b,b,c 五部分
    依旧按照之前的规则进行匹配

    1. 控制权 a,起始位置 0(整个正则表达式的最开始位置),匹配 a 成功,匹配位置变为 1,a 宽度为 1,控制权移交给 b{1,3}
     字符串: a
             ↑
     正  则: a
    
    1. 控制权 b{1,3},匹配位置 1,匹配 b 成功,匹配位置变为 2(第 1 个可回溯位置),b{1,3} 宽度 1-3 之间,默认为贪婪模式,控制权依旧在 b{1,3}
     字符串: a       b
             ↑       ↑
     正  则: a    b{1,3}
    
    1. 控制权 b{1,3},匹配位置 2,匹配 b 成功,匹配位置变为 3(第 2 个可回溯位置),b{1,3} 宽度1-3之间,默认为贪婪模式,控制权依旧在 b{1,3}
     字符串: a      bb
             ↑       ↑
     正  则: a    b{1,3}
    
    1. 控制权 b{1,3},匹配位置 3,匹配 b 成功,匹配位置变为 4,b{1,3} 宽度 1-3 之间,已经达到最大宽度,结束匹配,控制权移交给 b
     字符串: a      bbb
             ↑       ↑
     正  则: a    b{1,3}
    
    1. 控制权 b(前一个),匹配位置 4,匹配 c 失败,尝试进行回溯,最近的一个可回溯位置为 3 位置,此时 b{1,3} 这个时候只匹配头两个 bb 字符,从 3 位置开始,b(前一个)匹配第三个 b 字符,回溯成功,b 宽度为 1,控制权移交给 b(后一个),匹配位置变为4
     字符串: a      bb       b
             ↑       ↑       ↑
     正  则: a    b{1,3}     b
    
    1. 控制权 b(后一个),匹配位置 4,匹配 c 失败,尝试进行回溯,上一个可回溯位置为 2 位置,此时 b{1,3} 这个时候只匹配 b 一个字符,控制权移交给 b (前一个),从 2 位置匹配 b (前一个)成功,匹配位置变为 3,控制权移交给 b(后一个),从 3 位置匹配 b(后一个)成功,匹配位置变为 4,控制权移交给 c
     字符串: a       b       b      b
             ↑       ↑       ↑      ↑
     正  则: a    b{1,3}     b      b
    
    1. 控制权 c,匹配位置 4,匹配 c 成功,匹配位置变为 5
     字符串: a       b       b      b      c
             ↑       ↑       ↑      ↑      ↑
     正  则: a    b{1,3}     b      b      c
    
    1. 字符串结束,整个匹配过程结束

    回溯总结

    ①正则匹配规则 a, b{1,3}, b, b, c 分别匹配到了字符 a, b, b, b, c
    ②整个过程中由于 b{1,3} 存在可回溯位置,正则默认匹配规则为贪婪模式,b{1,3} 首先尽可能多的匹配,直到无法继续匹配的时候将控制权移交给下一个匹配字符
    ③当之后的匹配字符匹配失败的时候,正则表达式尝试从可回溯位置开始进行匹配,如果匹配依旧失败的话,再往前找上一个可回溯位置,直到表达式匹配成功
    ④如果已经没有任何可回溯位置能满足表达式,则整个表达式匹配失败,它将从上次匹配字符串的开始位置的下一个位置再次尝试匹配

    5.贪婪模式和非贪婪模式

    正则默认为贪婪模式,
    贪婪模式为尽可能多的匹配,但是非贪婪莫模式不能只解释为尽可能少的匹配

    let reg = /\w+?/;
    let str = 'abcd1234efgh5678';
    str.match(reg);
    // 结果为['a']
    

    这个时候确实可以理解为尽可能少的匹配

    再看一个例子

    reg = /\w+?\d/;
    str.match(reg);
    // 结果为['abcd1']
    

    这个时候如果按照尽可能少的匹配的原则,匹配到的应该是['d1']
    所以不能单纯的理解为尽可能少的匹配

    6.其他一些匹配回溯的例子

    let reg = /[a-z]{1,5}1/;
    let str = 'abcdef1ghijkl';
    

    表达式拆解为 [a-z]{1,5} 与 1 两部分

      a   b   c   d   e   f   1   g   h   i   j   k   l
    ↑   ↑   ↑   ↑   ↑   ↑   ↑   ↑   ↑   ↑   ↑   ↑   ↑   ↑
    0   1   2   3   4   5   6   7   8   9  10   11  12  13
    

    从0位置开始匹配

    1. [a-z]{1,5} 匹配 abcde 控制权移交给 1
     字符串:    abcde         f
                  ↑           ×
     正  则: [a-z]{1,5}       1
    
    1. 1 匹配f失败,尝试回溯,[a-z]{1,5} 匹配 abcd ,控制权交给 1
     字符串:    abcd         e
                  ↑          ×
     正  则: [a-z]{1,5}      1
    
    1. 1 匹配 e 失败,尝试回溯,[a-z]{1,5} 匹配 abc,控制权交给 1
     字符串:     abc         d
                  ↑          ×
     正  则: [a-z]{1,5}      1
    
    1. 1匹配d失败,尝试回溯,[a-z]{1,5}匹配ab,控制权交给1
     字符串:     ab         c
                  ↑         ×
     正  则: [a-z]{1,5}     1
    
    1. 1匹配c失败,尝试回溯,[a-z]{1,5}匹配a,控制权交给1
     字符串:      a         b
                  ↑         ×
     正  则: [a-z]{1,5}     1
    
    1. 1匹配b失败,[a-z]{1,5}无可回溯位置,[a-z]{1,5}前面的表达式也没有可回溯位置,匹配失败,即从字符串0位置开始匹配失败

    从1位置开始匹配

    1. [a-z]{1,5} 匹配 bcdef 控制权移交给 1
     字符串:  a      bcdef
                       ↑
     正  则:      [a-z]{1,5}
    
    1. 1 匹配 1 成功,控制权继续向后移交
     字符串:  a      bcdef       1
                       ↑         ↑
     正  则:      [a-z]{1,5}     1
    
    1. 个表达式结束,并且未声明为全局匹配,整个匹配过程结束

    7.回溯例子

    let str = 'abbbbbc';
    let reg = /ab{1,3}b{1,2}bc/;
    
    表达式                字符串
    
    a                     a
    ab{1,3}               ab (a, b{1,3}分别匹配a, b)
    ab{1,3}               abb (a, b{1,3}分别匹配a, bb)
    ab{1,3}               abbb (a, b{1,3}分别匹配a, bbb)
    ab{1,3}b{1,2}         abbbb (a, b{1,3}, b{1,2}分别匹配a, bbb, b)
    ab{1,3}b{1,2}         abbbbb (a, b{1,3}, b{1,2}分别匹配a, bbb, bb)
    ab{1,3}b{1,2}b        abbbbbc (a, b{1,3}, b{1,2}, b分别匹配a, bbb, bb, c, 匹配失败尝试进行回溯)
    ab{1,3}b{1,2}b        abbbbb (a, b{1,3}, b{1,2}, b分别匹配a, bbb, b, b)
    ab{1,3}b{1,2}bc       abbbbbc (a, b{1,3}, b{1,2}, b, c分别匹配a, bbb, b, b, c)
    

    相关文章

      网友评论

          本文标题:正则表达式匹配规则

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