我想找一个字符串是否包含只有一个出现的单词,
e.g。
String : `jjdhfoobarfoo` , Regex : `foo` --> false
String : `wewwfobarfoo` , Regex : `foo` --> true
String : `jjfffoobarfo` , Regex : `foo` --> true
多个foo
可能发生在字符串中的任何位置,因此它们可以是非连续的,
我在java中使用字符串foobarfoo
测试以下正则表达式匹配,但它不起作用并返回 true :
static boolean testRegEx(String str){
return str.matches(".*(foo)(?!.*foo).*");
}
我知道这个主题可能看似重复,但我很惊讶因为当我使用这个正则表达式时:(foo)(?!.*foo).*
它有效!
知道为什么会这样吗?
答案 0 :(得分:2)
使用两个锚定前瞻:
static boolean testRegEx(String str){
return str.matches("^(?=.*foo)(?!.*foo.*foo.*$).*");
}
有几个关键点是有一个负面的预测来检查2 foo的锚定开始,并且重要的是包含输入的结束。
答案 1 :(得分:1)
您可以使用此模式:
^(?>[^f]++|f(?!oo))*foo(?>[^f]++|f(?!oo))*$
它有点长但性能很好。
与 ashdflasd 字符串的经典示例相同:
^(?>[^a]++|a(?!shdflasd))*ashdflasd(?>[^a]++|a(?!shdflasd))*$
细节:
(?> # open an atomic group
[^f]++ # all characters but f, one or more times (possessive)
| # OR
f(?!oo) # f not followed by oo
)* # close the group, zero or more times
possessive quantifier ++
就像一个贪婪的量词+
,但不允许回溯。
atomic group (?>..)
就像一个非捕获组(?:..)
,但也不允许回溯。
这些功能在这里用于表演(记忆和速度),但子图案可以替换为:
(?:[^f]+|f(?!oo))*
答案 2 :(得分:1)
如果你想检查一个字符串是否只包含另一个字符串一次,这里有两个可能的解决方案,(一个是正则表达式,一个没有)
static boolean containsRegexOnlyOnce(String string, String regex) {
Matcher matcher = Pattern.compile(regex).matcher(string);
return matcher.find() && !matcher.find();
}
static boolean containsOnlyOnce(String string, String substring) {
int index = string.indexOf(substring);
if (index != -1) {
return string.indexOf(substring, index + substring.length()) == -1;
}
return false;
}
所有这些都很好。以下是您的示例演示:
String str1 = "jjdhfoobarfoo";
String str2 = "wewwfobarfoo";
String str3 = "jjfffoobarfo";
String foo = "foo";
System.out.println(containsOnlyOnce(str1, foo)); // false
System.out.println(containsOnlyOnce(str2, foo)); // true
System.out.println(containsOnlyOnce(str3, foo)); // true
System.out.println(containsRegexOnlyOnce(str1, foo)); // false
System.out.println(containsRegexOnlyOnce(str2, foo)); // true
System.out.println(containsRegexOnlyOnce(str3, foo)); // true
答案 3 :(得分:1)
正则表达式的问题在于,第一个.*
最初消耗整个字符串,然后退出,直到找到其他正则表达式可以匹配的位置。这意味着,如果字符串中有多个foo
,则正则表达式将始终与最后一个匹配。从那个位置来看,前瞻也将永远成功。
用于验证的正则表达式必须比用于匹配的正则表达式更精确。你的正则表达式失败了,因为.*
可以匹配sentinel字符串'foo'。您需要在您尝试匹配的匹配项之前和之后主动阻止foo
的匹配。 Casimir's answer显示了一种方法;这是另一个:
"^(?>(?!foo).)*+foo(?>(?!foo).)*+$"
效率不高,但我觉得阅读起来容易得多。事实上,你可以使用这个正则表达式:
"^(?!.*foo.*foo).+$"
效率低得多,但完整的正则表达式n00b可能会弄明白它的作用。
最后,请注意,这些正则表达式中没有一个 - 我的或Casimir的 - 使用了lookbehinds。我知道这似乎是工作的完美工具,但不是。事实上,lookbehind永远不应该是你达到的第一个工具。而不仅仅是Java。无论你使用什么样的正则表达式,在正常情况下匹配整个字符串几乎总是比使用lookbehinds更容易。而且通常也更有效率。
答案 4 :(得分:0)
有人回答了这个问题,但删除了它,
以下短代码正常运行:
static boolean testRegEx(String str){
return !str.matches("(.*?foo.*){0}|(.*?foo.*){2,}");
}
有关如何在正则表达式内部反转结果的任何想法?