3

我试图找出一个字符串是否包含一个单词的出现,

例如

String : `jjdhfoobarfoo` , Regex : `foo` --> false

String : `wewwfobarfoo` , Regex : `foo` --> true

String : `jjfffoobarfo` , Regex : `foo` --> true

多个foo's 可能出现在 string 的任何地方,因此它们可以是非连续的,

我在 java 中使用 string 测试以下正则表达式匹配foobarfoo,但它不起作用并且返回true

static boolean testRegEx(String str){
    return str.matches(".*(foo)(?!.*foo).*");
}

我知道这个主题可能看起来重复,但我很惊讶,因为当我使用这个正则表达式时:(foo)(?!.*foo).*它有效!

知道为什么会这样吗?

4

5 回答 5

2

使用两个锚定的前瞻:

static boolean testRegEx(String str){
    return str.matches("^(?=.*foo)(?!.*foo.*foo.*$).*");
}

几个关键点是,有一个否定的前瞻来检查 2 个 foo 的锚定开始,并且重要的是包含输入的结束。

于 2013-06-29T12:40:52.113 回答
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
于 2013-06-28T23:31:16.513 回答
1

您的正则表达式的问题在于,第一个.*最初消耗整个字符串,然后后退,直到它找到一个正则表达式的其余部分可以匹配的位置。这意味着,如果字符串中有多个foo,您的正则表达式将始终匹配最后一个。从那个位置开始,前瞻也将始终成功。

用于验证的正则表达式必须比用于匹配的正则表达式更精确。您的正则表达式失败,因为.*可以匹配标记字符串“foo”。您需要积极阻止foo您尝试匹配之前和之后的匹配。 卡西米尔的回答显示了一种方法。这是另一个:

"^(?>(?!foo).)*+foo(?>(?!foo).)*+$"

它效率不高,但我认为它更容易阅读。事实上,您可能可以使用这个正则表达式:

"^(?!.*foo.*foo).+$"

它的效率要低得多,但是一个完整的正则表达式 n00b 可能会弄清楚它的作用。

最后,请注意,这些正则表达式——我的或 Casimir 的——都没有使用后视。我知道这似乎是完成这项工作的完美工具,但不是。事实上,lookbehind 永远不应该是您使用的第一个工具。不仅仅是在 Java 中。无论您使用哪种正则表达式风格,以正常方式匹配整个字符串几乎总是比使用lookbehinds 更容易。而且通常也更有效率。

于 2013-06-29T12:00:26.830 回答
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

所有格量词 ++就像一个贪婪的量词,+但不允许回溯。

原子组 (?>..)就像一个非捕获组,(?:..)但也不允许回溯。

这些特性在这里用于性能(内存和速度),但子模式可以替换为:

(?:[^f]+|f(?!oo))*
于 2013-06-28T23:22:39.847 回答
-1

有人回答了这个问题,但删除了它,

以下短代码可以正常工作:

static boolean testRegEx(String str){
    return !str.matches("(.*?foo.*){0}|(.*?foo.*){2,}");
}

关于如何在正则表达式本身内部反转结果的任何想法?

于 2013-06-29T00:32:54.387 回答