• 02量词的匹配模式


    02-量词的匹配模式

    正则中的三种模式,贪婪匹配非贪婪匹配独占模式

    为什么会有贪婪与非贪婪模式?

    为节约cpu资源

    img

    在这 6 种元字符中,我们可以用 {m,n} 来表示 (*)(+)(?) 这 3 种元字符:

    元字符 同义表示 示例
    * {0,} ab* 可以匹配 a 或 abbb……
    + {1,} ab+ 可以匹配 ab 或 abbb……
    ? {0,1} (\+86-)?\d{11} 可以匹配 +86-138456728433 或者 138456728433

    贪婪、非贪婪与独占模式

    贪婪匹配(Greedy)

    在正则中,表示次数的量词默认是贪婪的,在贪婪模式下,会尝试尽可能最大长度去匹配。

    img
    >>> import re
    >>> re.findall(r'a*', 'aaabb')  # 贪婪模式
    ['aaa', '', '', '']
    >>> re.findall(r'a*?', 'aaabb') # 非贪婪模式
    ['', 'a', '', 'a', '', 'a', '', '', '']
    
    img

    从下面这个示例中,我们可以很容易看出两者对比上的差异。左右的文本是一样的,其中有两对双引号。不同之处在于,左边的示例中,不加问号时正则是贪婪匹配,匹配上了从第一个引号到最后一个引号之间的所有内容;而右边的图是非贪婪匹配,找到了符合要求的结果。

    img

    独占模式

    独占模式(Possessive)不管是贪婪模式,还是非贪婪模式,都需要发生回溯才能完成相应的功能。但是在一些场景下,我们不需要回溯,匹配不上返回失败就好了,因此正则中还有另外一种模式,独占模式,它类似贪婪匹配,但匹配过程不会发生回溯,因此在一些场合下性能会更好。

    # 贪婪模式
    regex = “xy{1,3}z” # 我得理解 没有 `?` 就是莽,一脚踩下去(默认符合当前条件|现状 >= 当前条件) 不行再退回 匹配下一条件
    
    text = “xyyz”
    

    在匹配时,y{1,3}会尽可能长地去匹配,当匹配完 xyy 后,由于 y 要尽可能匹配最长,即三个,但字符串中后面是个 z 就会导致匹配不上,这时候正则就会向前回溯,吐出当前字符 z,接着用正则中的 z 去匹配。

    img
    # 非贪婪模式
    regex = “xy{1,3}?z” # 有 `?` 就是投石问路,当前条件以经部分满足(现状 =< 当前条件)查看下一条件是否满足 不行再退回,匹配当前条件
    text = “xyyz”
    

    由于 y{1,3}? 代表匹配 1 到 3 个 y,尽可能少地匹配。匹配上一个 y 之后,也就是在匹配上 text 中的 xy 后,正则会使用 z 和 text 中的 xy 后面的 y 比较,发现正则 z 和 y 不匹配,这时正则就会向前回溯,重新查看 y 匹配两个的情况,匹配上正则中的 xyy,然后再用 z 去匹配 text 中的 z,匹配成功。

    img

    独占模式

    具体的方法就是在量词后面加上加号(+)

    regex = “xy{1,3}+yz” # 尽可能多滴匹配(莽着走)错了也不会回头
    text = “xyyz”
    

    img

    需要注意的是 Python 和 Go 的标准库目前都不支持独占模式,会报错,如下所示:

    Javascript貌似也不支持,报错信息:Nothing to repeat

    >>> import re
    >>> re.findall(r'xy{1,3}+yz', 'xyyz')
    error: multiple repeat at position 7
    # 报错显示,加号(+)被认为是重复次数的元字符了。如果要测试这个功能,我们可以安装 PyPI 上的 regex 模块
    #========================================================================#
    
    # 注意:需要先安装 regex 模块,pip install regex
    
    >>> import regex
    >>> regex.findall(r'xy{1,3}z', 'xyyz')  # 贪婪模式
    ['xyyz']
    >>> regex.findall(r'xy{1,3}+z', 'xyyz') # 独占模式
    ['xyyz']
    >>> regex.findall(r'xy{1,2}+yz', 'xyyz') # 独占模式
    [] # 不回溯导致 'xy{1,2}+yz' z 前面 的 y 匹配不上 ,返回空字符串
    

    你也可以使用 Java 或 Perl 等其它语言来测试独占模式,查阅相关文档,看一下你所用的语言对独占模式的支持程度。

    如果你用 a{1,3}+ab 去匹配 aaab 字符串,a{1,3}+ 会把前面三个 a 都用掉,并且不会回溯,这样字符串中内容只剩下 b 了,导致正则中加号后面的 a 匹配不到符合要求的内容,匹配失败。

    如果是贪婪模式 a{1,3} 或非贪婪模式 a{1,3}? 都可以匹配上。

    img

    这里我简单总结一下,独占模式性能比较好,可以节约匹配的时间和 CPU 资源,但有些情况下并不能满足需求,要想使用这个模式还要看具体需求(比如我们接下来要讲的案例),另外还得看你当前使用的语言或库的支持程度。

    模式示例

    ^([符合要求的组成1]|[符合要求的组成2])+$ 
    # 脱字符(^)代表以这个正则开头,
    # 美元符号($)代表以正则结尾,我们后面会专门进行讲解。
    # 这里可以先理解成整个店铺名称要能匹配上正则,即起到验证的作用。
    

    最后总结

    img

    正则中量词默认是贪婪匹配,如果想要进行非贪婪匹配需要在量词后面加上问号。贪婪和非贪婪匹配都可能会进行回溯,独占模式也是进行贪婪匹配,但不进行回溯,因此在一些场景下,可以提高匹配的效率,具体能不能用独占模式需要看使用的编程语言的类库的支持情况,以及独占模式能不能满足需求。

    练习

    有一篇英文文章,里面有很多单词,单词和单词之间是用空格隔开的,在引号里面的一到多个单词表示特殊含义,即引号里面的多个单词要看成一个单词。现在你需要提取出文章中所有的单词。我们可以假设文章中除了引号没有其它的标点符号,有什么方法可以解决这个问题呢?如果用正则来解决,你能不能写出一个正则,提取出文章中所有的单词呢(不要求结果去重)?

    we found “the little cat” is in the hat, we like “the little cat”其中 the little cat 需要看成一个单词

    // 文字中的双引号是 中文!

    \w+|“[^”]*” 
    # 建议第一种
    # \w 表示英文单词 (单个字母也算)
    # \W 表示非英文单词
    # ^ 表示取反 `[^”]` 表示 匹配中文右引号之外的所有字符
    

  • 相关阅读:
    oracle中delete、truncate、drop的区别
    js获取当前日期时间
    Linux 命令大全
    oracle sql语言模糊查询
    ibatis<iterate>标签
    ibatis中使用like模糊查询
    编辑距离12 · Edit Distance12
    分割回文串 II · Palindrome Partitioning II
    单词拆分 I · Word Break
    300最长上升子序列 · Longest Increasing Subsequence
  • 原文地址:https://www.cnblogs.com/OwlInTheOaktree/p/16075359.html
Copyright © 2020-2023  润新知