选择符号

用竖线符号来进行选择


字符类中已经解释了如何使用字符类来匹配多个可能字符中的单个字符。选择模式是相似的。我们可以使用选择符来匹配多个可能的正则表达式中的单个正则表达式。  

如果要搜索文字文本cat或dog ,请用竖线或竖线符号将两个选项分开:cat|dog。如果我们需要更多选项,只需展开列表:cat|dog|pig。     

在正则表达式所有的运算符中,选择运算符的优先级最低。也就是说,它告诉正则表达式引擎将竖线|左侧的所有内容或竖线|右侧的所有内容匹配。如果要限制选择的范围,则需要使用括号进行分组。如果我们想改进第一个示例以仅匹配整个单词,则需要使用\b(cat|dog)\b。这告诉正则表达式引擎先找到一个单词边界,然后是cat或dog ,然后是另一个单词边界。如果我们省略了括号,那么正则表达式引擎将搜索单词边界,其后是cat,或者是dog后跟一个单词边界。 

正则表达式引擎是懒惰的


我们已经解释过,正则表达式引擎 非常懒惰的。一旦找到有效的匹配项,它将立即停止搜索,也就是我们提到的最左匹配原则。结果是,在某些情况下,选择方案的顺序很重要。假设我们要使用正则表达式来匹配编程语言中的函数名称列表:Get,GetValue,Set或SetValue。显而易见的解决方案是Get|GetValue|Set|SetValue。让我们看看当字符串为SetValue时正则表达式的引擎是如何工作的。   

正则表达式引擎从正则表达式中的第一个标记G开始,并从字符串中的第一个字符S开始。匹配失败。但是,正则表达式引擎在启动之前解析了整个正则表达式。因此,它知道此正则表达式使用了选择模式,所以整个正则表达式尚未失败。因此,它继续使用第二个选项,即正则表达式中的第二个G。,和第一个字符S进行匹配。再次失败。下一个标记是正则表达式中的第一个S。匹配成功,引擎继续字符串中的下一个字符e以及正则表达式中的下一个token e。e匹配e 。下一个token t与下一个字符t匹配。

至此,选项中的第三个选项已成功匹配。因为正则表达式引擎非常懒惰,其实我觉得说它急功近利可能比较合适,它认为,只要其中一种选择成功,整个选择项就已经成功匹配。所以,在这个例子中,整个正则表达式已经成功匹配字符串SetValue中的Set。   

与我们的预期相反,正则表达式与整个字符串不匹配。有几种解决方案。一种选择是考虑到正则表达式引擎的懒惰,并更改选项的顺序。如果我们使用GetValue |Get|SetValue|Set ,SetValue在Set之前,那么引擎匹配整个字符串。我们还可以将这四个选项合并为两个,并使用问号使它们的一部分成为可选内容:Get(Value)?|Set(Value)?。因为问号是贪婪的,所以尝试在Set之前尝试SetValue 。          

最好的选择可能是表达一个事实,即我们只想匹配完整的单词。如果字符串为SetValueFunction,则我们不希望匹配Set或SetValue。因此,解决方案是\b(Get|GetValue|Set|SetValue)\b\b(Get(Value)?|Set(Value)?)\b 。由于所有选项都有相同的结尾,因此我们可以进一步优化到\b(Get|Set)(Value)?\b 。

文本导向引擎返回最长匹配


正则导向的引擎和文本导向的引擎之间的选择模式是不同的。当文本导向的引擎尝试将正则Get|GetValue|Set|SetValue应用于字符串SetValue上时,它将在字符串的开头尝试正则表达式的所有排列。它非常的高效,没有任何回溯行为。它可以看到正则表达式从而在字符串的开头找到匹配项,并且匹配的文本可以是Set或SetValue 。因为文本导向的引擎对正则表达式进行了整体评估,所以它没有一个替代项在另一个替代项之前列出的概念。但是它必须选择要返回的匹配项。它始终返回最长的匹配项,所以,它会返回SetValue 。

POSIX需要最长的匹配


POSIX标准才不会关心是要选择一个文本导向还是正则表达式导向的引擎。它需要使用正则表达式导向的引擎来评估包含反向引用的BRE。但是,也可以使用文本导向引擎来评估没有反向引用的BRE或ERE。但是POSIX标准确实要求返回最长的匹配项,即使使用正则表达式导向的引擎也是如此(上面我们介绍过正则表达式导向的引擎是很急功近利的)。这样的引擎不能急于求成。即使找到匹配项,它也必须继续尝试所有替代项,以便找到最长的替代项。

当正则表达式包含多个量词或量词和选择的组合时,这可能会导致非常差的性能,因为必须尝试所有组合来查找最长的匹配项。  

TclGNU的正则也是以这种方式工作的。      

查看笔记

扫码一下
查看教程更方便