zoukankan      html  css  js  c++  java
  • 正则表达式量词的三种模式

    Greedy 数量词
    X? X,一次或一次也没有
    X* X,零次或多次
    X+ X,一次或多次
    X{n} X,恰好 n
    X{n,} X,至少 n
    X{n,m} X,至少 n 次,但是不超过 m
     
    Reluctant 数量词
    X?? X,一次或一次也没有
    X*? X,零次或多次
    X+? X,一次或多次
    X{n}? X,恰好 n
    X{n,}? X,至少 n
    X{n,m}? X,至少 n 次,但是不超过 m
     
    Possessive 数量词
    X?+ X,一次或一次也没有
    X*+ X,零次或多次
    X++ X,一次或多次
    X{n}+ X,恰好 n
    X{n,}+ X,至少 n
    X{n,m}+ X,至少 n 次,但是不超过 m

    import java.util.Arrays;

    public class Test {
        
    public static void main(String args[]) {
                String t
    ="x123xxxxxx123";
                Pattern p
    =Pattern.compile(".*123");
                Matcher m
    =p.matcher(t);
                System.out.println(
    "===========贪婪模式================");
                
    while(m.find()){                
                    System.out.println(
    "开始"+m.start());
                    System.out.println(m.group());
                    System.out.println(
    "结束"+m.end());
                }
               
                
                Pattern p1
    =Pattern.compile(".*?123");
                Matcher m1
    =p1.matcher(t);
                System.out.println(
    "===========懒惰模式================");
                
    while(m1.find()){                
                    System.out.println(
    "开始"+m1.start());
                    System.out.println(m1.group());
                    System.out.println(
    "结束"+m1.end());
                }
                
                
                Pattern p2
    =Pattern.compile(".*+123");
                Matcher m2
    =p2.matcher(t);
                System.out.println(
    "===========侵入模式================");
                
    while(m1.find()){                
                    System.out.println(
    "开始"+m2.start());
                    System.out.println(m2.group());
                    System.out.println(
    "结束"+m2.end());
                }
                
        }
    }


    以下为输出:
    ===========贪婪模式================
    开始0
    x123xxxxxx123
    结束13
    ===========懒惰模式================
    开始0
    x123
    结束4
    开始4
    xxxxxx123
    结束13
    ===========侵入模式================

    讨论:
          贪婪模式:它的特性是一次性地读入整个字符串,如果不匹配就吐掉最右边的一个字符再匹配,直到找到匹配的字符串或字符串的长度为0为止。它的宗旨是读尽可能多的字符,所以当读到第一个匹配时就立刻返回。

          此例中,正则“.*123”中的“.*”首先消耗了整个字符串,所以匹配肯定不成功。然后匹配器一次后退一个字符,一直后退到最右侧出现“123”为止,这里匹配成功并且搜索停止。或者这样理解,为了整个表达式能匹配成功,“.*”虽然可以读入整个表达式,但它还是“主动让出”123这3个它本可读入的字符空间,从这点可看出贪婪模式虽贪婪但还是挺深明大义的。 

     
        懒惰模式:它的特性是从字符串的左边开始,试图不读入字符串中的字符进行匹配,失败,则多读一个字符,再匹配,如此循环,当找到一个匹配时会返回该匹配的字符串,然后再次进行匹配直到字符串结束。
          
          此例中,正则“.*?123”中的“.*?”,它试图0消耗的匹配字符串,但失败,不得不再读入(消耗)一个字符再进行匹配,这时x123符合要求,返回之(x123),再读入,直到末尾时找到xxxxxx123匹配又返回之,这时已经没有东西可读了,结束匹配。
      
          此例还返回
                      开始4
                      xxxxxx123
                      结束13
          而不是最后的x123,这说明为了成功匹配,懒惰的它也不懒惰的多读了一连串x让后面的123匹配。
          侵入模式:它和贪婪模式很相似,不同点是它不会往会吐。
          
          此例中,正则“.*+123”中的“.*+”首先消耗了整个字符串,所以匹配不成功,由于它不回吐,所以匹配结束。

    结论:懒惰模式的本性是懒惰的,贪婪模式的本性是贪婪的,但当在它们周围出现其它伙伴时,懒惰模式就不那么懒惰,贪婪模式就不那么贪婪了。

    还有一点:
          开始0
          x123
          结束4
          开始4
          xxxxxx123
          结束13
    可以看出,一个结束就意味着下一个的开始,这暗示了,这个结束位置的字符并没有被消耗掉(如果消耗掉的话下一个的开始应该是5了)。

    模式的陷阱:

    当一个表达式中,有多个非贪婪匹配时,或者多个未知匹配次数的表达式时,这个表达式将可能存在效率上的陷阱。有时候,匹配速度慢得莫名奇妙,甚至开始怀疑正则表达式是否实用。

    效率陷阱的产生

       “如果少匹配就会导致整个表达式匹配失败的时候,与贪婪模式类似,非贪婪模式会最小限度的再匹配一些,以使整个表达式匹配成功。”

        具体的匹配过程是这样的:

    1. "非贪婪部分" 先匹配最少次数,然后尝试匹配 "右侧的表达式"。
    2. 如果右侧的表达式匹配成功,则整个表达式匹配结束。如果右侧表达式匹配失败,则 "非贪婪部分" 将增加匹配一次,然后再尝试匹配 "右侧的表达式"。
    3. 如果右侧的表达式又匹配失败,则 "非贪婪部分" 将再增加匹配一次。再尝试匹配 "右侧的表达式"。
    4. 依此类推,最后得到的结果是 "非贪婪部分" 以尽可能少的匹配次数,使整个表达式匹配成功。或者最终仍然匹配失败。

        当一个表达式中有多个非贪婪匹配,以表达式 "d(\w+?)d(\w+?)z" 为例,对于第一个括号中的 "\w+?" 来说,右边的 "d(\w+?)z" 属于它的 "右侧的表达式",对于第二个括号中的 "\w+?" 来说,右边的 "z" 属于它的 "右侧的表达式"。

        当 "z" 匹配失败时,第二个 "\w+?" 会 "增加匹配一次",再尝试匹配 "z"。如果第二个 "\w+?" 无论怎样 "增加匹配次数",直至整篇文本结束,"z" 都不能匹配,那么表示 "d(\w+?)z" 匹配失败,也就是说第一个 "\w+?" 的 "右侧" 匹配失败。此时,第一个 "\w+?" 会增加匹配一次,然后再进行 "d(\w+?)z" 的匹配。循环前面所讲的过程,直至第一个 "\w+?" 无论怎么 "增加匹配次数",后边的 "d(\w+?)z" 都不能匹配时,整个表达式才宣告匹配失败。

        其实,为了使整个表达式匹配成功,贪婪匹配也会适当的“让出”已经匹配的字符。因此贪婪匹配也有类似的情况。当一个表达式中有较多的未知匹配次数的表达式时,为了让整个表达式匹配成功,各个贪婪或非贪婪的表达式都要进行尝试减少或增加匹配次数,由此容易形成一个大循环的尝试,造成了很长的匹配时间。本文之所以称之为“陷阱”,因为这种效率问题往往不易察觉。

        举例:"d(\w+?)d(\w+?)d(\w+?)z" 匹配 "ddddddddddd..." 时,将花费较长一段时间才能判断出匹配失败 。

    效率陷阱的避免:

        避免效率陷阱的原则是:避免“多重循环”的“尝试匹配”。并不是说非贪婪匹配就是不好的,只是在运用非贪婪匹配的时候,需要注意避免过多“循环尝试”的问题。

        情况一:对于只有一个非贪婪或者贪婪匹配的表达式来说,不存在效率陷阱。也就是说,要匹配类似 "<td> 内容 </td>" 这样的文本,表达式 "<td>([^<]|<(?!/td>))*</td>" 和 "<td>((?!</td>).)*</td>" 和 "<td>.*?</td>" 的效率是完全相同的。

        情况二:如果一个表达式中有多个未知匹配次数的表达式,应防止进行不必要的尝试匹配。

        比如,对表达式 "<script language='(.*?)'>(.*?)</script>" 来说, 如果前面部分表达式在遇到 "<script language='vbscript'>" 时匹配成功后,而后边的 "(.*?)</script>" 却匹配失败,将导致第一个 ".*?" 增加匹配次数再尝试。而对于表达式真正目的,让第一个 ".*?" 增加匹配成“vbscript'>”是不对的,因此这种尝试是不必要的尝试。

        因此,对依靠边界来识别的表达式,不要让未知匹配次数的部分跨过它的边界。前面的表达式中,第一个 ".*?" 应该改写成 "[^']*"。后边那个 ".*?" 的右边再没有未知匹配次数的表达式,因此这个非贪婪匹配没有效率陷阱。于是,这个匹配脚本块的表达式,应该写成:"<script language='([^']*)'>(.*?)</script>" 更好。


     

  • 相关阅读:
    SSIS数据同步实践
    不同实例下同构表数据同步验证
    Performance Analysis of Logs (PAL) Tool
    scrapy框架_3持久化存储
    scrapy框架_2数据解析案例_最新糗事百科案例
    scrapy框架_简单基础命令操作
    Selenium 模块3经典案例_规避检测_js写入破解服务器Selenium识别 模拟登陆12306登陆
    Selenium 模块2_iframe处理_动作链
    Selenium 模块
    Flask_模板
  • 原文地址:https://www.cnblogs.com/Fskjb/p/1566760.html
Copyright © 2011-2022 走看看