尝试使用正则表达式构建逻辑“与”运算时,我们有几种方法可以遵循。 第一种方法似乎很明显,但是如果考虑一下,默认情况下正则表达式是逻辑“和”的。 正则表达式中的每个顺序字符都一起“与”。 如果您可以按顺序表达您的陈述,那么工作已经为您完成。
但是,既然您已经搜索了这么远,我们可以假设您正在寻找更高级的东西。 为方便起见,我们有两个选择:我们可以使用“ lookaheads”,或者如果您使用的任何工具或语言支持,也可以使用单独的正则表达式执行第二次匹配。
先行查找实现逻辑“与”
先行查找和后向操作本质上是可以放在正则表达式上的额外约束。 您可以指定成功匹配需要满足的其他模式。 以下是预读表达式的示例。
(?=.*word1)(?=.*word2)(?=.*word3)
请注意,每个表达式都包含.*
–这是因为先行查找对位置敏感,并且从它们在模式中出现的位置开始匹配; 因此,例如,如果我们有一个类似于以下模式的模式:
^Start (?=.*kind)(?=.*good)(?=.*word).* deed$
此模式将匹配"Start with a good word and end with a kind deed
" and “Start with a kind word and end with a good deed
”
总结:一旦开始第一个look-ahead
,就保存表达式中的匹配位置。 第一个look-ahead
中的.*
匹配获得kind
之前需要的多个字符; 匹配位置被重置,并且下一个look-ahead
向前搜索“good
”; 最后但并非最不重要的一点是,我们最终的look-ahead
将搜索“word
”,然后像往常一样恢复模式匹配。 匹配继续使用表达式的基本.*
,并继续通过“deed”匹配到字符串的末尾。
这样做的原因是,如上所述,因为在评估每次环视后都会重置匹配位置。 这意味着我们相邻的“and”look-ahea
表达式的顺序并不重要。 但是,如果我们将条件之一移至.*
之后,则会看到不同的结果:
^Start (?=.*kind).*(?=.*good) deed.$
现在(?=.*good)
排在我们的catch-all
之后,我们之前的字符串都不匹配,因为在评估.*
之后不可能存在“good
”。
提示,如果要匹配整个单词而不是较长单词的部分字符串,则需要在语句中添加单词边界:
^Start (?=.*\bkind\b)(?=.*\bgood\b)(?=.*\bword\b).* deed$
用您的语言实现逻辑“与”
如果所有其他方法都失败了,那么您应该始终简单地执行另一场比赛,并使用所选编程语言的本机“and”逻辑功能将结果组合在一起,您总是会感到自在。 对于其他人来说,将来通常更容易维护,并且对中等数据集的性能影响很小。 例如,在Java中:
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class RegexTest
{
@Test
public void testRegex()
{
assertTrue(stringMatches("Start with a good word and end with a kind deed."));
assertTrue(stringMatches("Start with a kind word and end with a good deed."));
assertFalse(stringMatches("Start with a deed."));
}
private boolean stringMatches(String string)
{
return string.matches("^Start .* deed.$") && string.matches(".*good.*") && string.matches(".*kind.*");
}
}