写点什么

正则表达式.02 - 量词与贪婪

用户头像
insight
关注
发布于: 2021 年 03 月 07 日
正则表达式.02 - 量词与贪婪

正则中有三种模式

  • 贪婪模式

  • 非贪婪模式

  • 独占模式


这些模式会改变量词的匹配行为,比如:

  • 字符是匹配一次还是多次

  • 匹配长度是尽可能长还是尽可能短


贪婪匹配(Greedy)

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


首先,我们来看一下在字符串 aaabb 中使用正则 a* 的匹配过程。

a* 在匹配开头的 a 时,会尝试尽量匹配更多的 a,直到第一个字母 b 不满足要求为止,匹配上三个 a,后面每次匹配时都得到了空字符串。


相信看到这里你也发现了,贪婪模式的特点就是尽可能进行最大长度匹配。


所以要不要使用贪婪模式是根据需求场景来定的。如果我们想尽可能最短匹配呢?那就要用到非贪婪匹配模式了。


非贪婪匹配(Lazy)

那么如何将贪婪模式变成非贪婪模式呢?我们可以在量词后面加上英文的问号 (?),正则就变成了 a*?。此时的匹配结果如下:



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

到这里你可能就明白了,非贪婪模式会尽可能短地去匹配,我把这两者之间的区别写到了下面这张图中。

贪婪与非贪婪的区别

贪婪与非贪婪模式都必须满足匹配次数的要求才能匹配上。

  • 贪婪模式,简单说就是尽可能进行最长匹配。

  • 非贪婪模式呢,则会尽可能进行最短匹配。

正是这两种模式产生了不同的匹配结果。

简单的例子

看一个简单的例子:同样是 aaabb


贪婪模式下的结果是这样的:

非贪婪的结果是这样的:

复杂一些的例子

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


独占模式

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

回溯是什么?

回溯是指,当正则按规则对一个字母匹配失败之后,从该字母开始重新进行规则匹配。

而非回溯则是指,当正则按规则对一个字母匹配失败之后,会从下一个字母开始进行规则的重新匹配。

举个例子

贪婪模式匹配

regex = “xy{1,3}z”

text = “xyyz”


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

非贪婪模式匹配

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,匹配成功。

独占模式匹配

独占模式和贪婪模式很像,独占模式会尽可能多地去匹配,如果匹配失败就结束,不会进行回溯,这样的话就比较节省时间。具体的方法就是在量词后面加上加号(+)。


regex = “xy{1,3}+yz”

text = “xyyz”


三种模式的差异

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

语言支持

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

  • 可以使用 Java 或 Perl 等其它语言来测试独占模式

  • 查阅相关文档,看一下你所用的语言对独占模式的支持程度。

总结

独占模式性能比较好,可以节约匹配的时间和 CPU 资源,但有些情况下并不能满足需求,要想使用这个模式还要看具体需求,另外还得看你当前使用的语言或库的支持程度。


用户头像

insight

关注

不要混淆行动与进展、忙碌与多产。 2018.11.17 加入

永远都是初学者

评论

发布
暂无评论
正则表达式.02 - 量词与贪婪