12-量词

12.1 星号*和加号+

之前我们已经介绍过问号?这个量词,引擎会把它之前的token匹配一次或者零次,换句话说问号之前的token是可选的。

星号的意思是匹配0次或者多次它之前的token,而加号的意思说匹配1次或者多次。例如<[A-Za-z][A-Za-z0-9]*>可以匹配一个不包含属性的HTML标签。其中尖括号是字面量字符。第一个字符集匹配一个字母。第二个字符集匹配一个字母或是数字。第二个字符集可以重复匹配,因为这里使用了星号所以第二个字符集可以不匹配任何字符。也就是说这个表达式可以匹配单个字母的标签 <B>,同时也可以匹配 <HTML>。其中第一个字符集匹配到 H,第二个字符集重复匹配了三次分别匹配到T,M,L。

也可是使用<[A-Za-z0-9]+>来搜索HTML标签,但是这个表达式的缺点是它也可以匹配到<1>这种非法的标签。

12.2 限定重复次数

我们可以使用花括号来限定token重复匹配的次数。它的语法是{min,max},其中min是一个大于等于0的整数,而max是大于等于min的整数。如果使用了逗号,且省略max则表示max是无穷大。所以{0,1}等同于?{0,}等同于*{1,}等同于+。如果同时省略逗号和 max,则表示限定重复 min 次。

你可以使用\b[1-9][0-9]{3}\b匹配1000到9999中的数字。\b[1-9][0-9]{2,4}\b则可以匹配100到99999之间的整数。注意这里使用了词语边界

12.3 量词是贪婪的!

假设你要匹配HTML标签,并且你知道匹配的数据都是合法的标签。也就是测试数据中不包含尖括号非法使用的情况,只要出现在一对尖括号中间的就是HTML标签。

很多正则表达式的新手会首先尝试<.+>。如果用这个表达式匹配字符串This is a <EM>first</EM> test的话,他们会得到令人意外的结果。或许你以为他会匹配到<EM></EM>

但是结果并非如此,他匹配到的是<EM>first</EM>。原因就在于加号是贪婪的,也就是说加号使得引擎尽可能多的去重复匹配加号之前的token。也就是说只有当整个表达式匹配失败的时候引擎才开始“回退”,引擎将回到加号并且放弃最后一次迭代,用剩下的表达式匹配剩余的字符串。在下一节中我们详细展开这个过程,在这之后我们会提出两种方法来解决上面遇到的问题。

要注意:和加号一样,星号和花括号也是贪婪的。

12.4 贪婪模式在引擎中原理

在上一个例子中,第一个匹配的token是字面量字符<,而第一个匹配成功的字符是字符串中的第一个 < 。接下来匹配的token是.,它可以匹配除了换行符以外的任何字符,它的后面是一个**可以匹配无限多次并且星号是贪婪的。所以引擎会尽可能多次的匹配。接下来出现的字符是 E 它和.匹配成功了,所以引擎进一步把下一个字符和.匹配。E 后面的 M 也匹配成功了。接下来的字符是 > ,现在你因该已经看出问题来了吧。由于 > 也是一个普通字符,所以.也能匹配它。实际上.可以匹配所有剩下的字符。直到正则表达式末尾的void,.才匹配失败。接下来引擎匹配最后一个token>

此时正则表达式的前半部分<.+已经匹配到了字符串中的<EM>first</EM> test,并且此时引擎将把token>和字符串中最后一个位置匹配,匹配不能成功。此时引擎知道.可能不需要重复那么多次,此时引擎不会立即确认匹配失败,而是原路返回一个位置。也就是说引擎会减少一次重复的次数,然后继续匹配表达式的剩余部分。

所以此时.+匹配到的结果是EM>first</EM> tes。下一个要匹配的token是>,但是此时下一个要匹配的字符是 t 。这一次的回退还是没有匹配成功。引擎继续回退到<EM>first</EM> te,此时还是不能匹配上。又经过5次回退后,引擎已经回退到<EM>first</EM,此时token>可以和下一个字符>匹配。随着最后一个token匹配成功,引擎得到最终的匹配结果<EM>first</EM>

由于引擎会返回最先匹配到的结果,所以一旦匹配成功引擎就不会继续回退,即使能匹配到更短的结果。所以在贪婪模式下,引擎会匹配到最靠左且最长的结果。

12.5 使用惰性匹配(非贪婪匹配)而不是贪婪匹配

你可以使用惰性匹配来快速修复这个问题。它的语法是在量词后面添加一个问号。例如.??.*?.+?.{0,1}?。那么在刚才那里例子中,我们可以这么修改<.+?>。让我们再看看引擎内部的工作方式。

和之前一样第一个token<匹配到了字符串中的首个 < 。下一个token是.,不过这一次.后面的量词是惰性的。引擎会尽可能少的匹配token.的次数。这个例子中量词+至少需要匹配一次。所以.匹配到了字符 E 。接下来引擎匹配token>,因为 E 后面是 M ,所以不能匹配上。此时引擎并不能确定匹配失败,由于前一个token是量词,所以此时引擎进行一次回退。但是和贪婪模式不同的是,惰性模式会尝试增加一次重复匹配的.。在此次退回之后token.+匹配到字符 EM。接下来引擎把下一个token>和字符 > 匹配。现在所有的token已经匹配完成,引擎匹配到了字符 <EM>。我们离正确答案似乎又近了一步。

12.6 惰性匹配的替代方案

的确存在比惰性匹配更好的方式,那就是使用字符集取反<[^>]+>。这种方式更好的原因是因为它避免了回溯,如果测试数据中的标签都是合法的,使用字符集取反可以避免回溯。回溯会影响引擎的性能,你可能无法再小规模测试中感觉到这种差异,但是如果你在一个循环语句中使用的话那就可能导致引擎崩溃。

文本驱动的引擎不支持回溯功能,所以这种引擎也不支持惰性匹配。


如果文章出现错误,请给我提Issues - -
Github地址

原文

猜你喜欢

转载自blog.csdn.net/billll/article/details/85067878
今日推荐