3

文字列に単語が1 回だけ含まれているかどうかを調べようとしています。

例えば

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

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

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

複数fooの は string のどこにでも出現する可能性があるため、連続していなくてもかまいません。

Java で string を使用して次の正規表現マッチングをテストしましたfoobarfooが、機能せず、trueを返します。

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

このトピックが重複しているように見えるかもしれませんが、驚いたことに、この正規表現を使用する(foo)(?!.*foo).*と機能します。

なぜこれが起こるのですか?

4

5 に答える 5

1

正規表現の問題は、最初.*に最初に文字列全体を消費し、残りの正規表現が一致する場所が見つかるまで後退することです。つまりfoo、文字列に複数ある場合、正規表現は常に最後のものと一致します。そして、その位置から、先読みも常に成功します。

検証に使用する正規表現は、照合に使用する正規表現よりも正確である必要があります。.*がセンチネル文字列「foo」と一致する可能性があるため、正規表現は失敗しています。foo一致させようとしているものの前後 の一致を積極的に防ぐ必要があります。カシミールの答えは、それを行う1つの方法を示しています。ここに別のものがあります:

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

それほど効率的ではありませんが、はるかに読みやすいと思います。実際、おそらく次の正規表現を使用できます。

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

これははるかに非効率的ですが、完全な正規表現 n00b はおそらくそれが何をするかを理解するでしょう。

最後に、これらの正規表現 (私のものも Casimir のものも) はどれも後読みを使用していないことに注意してください。仕事に最適なツールのように思えますが、そうではありません。実際、後読みは最初に使用するツールであってはなりません。Javaだけではありません。どのような正規表現を使用する場合でも、後読みを使用するよりも通常の方法で文字列全体を一致させる方がほとんどの場合簡単です。また、通常ははるかに効率的です。

于 2013-06-29T12:00:26.830 に答える
1

文字列に別の文字列が含まれているかどうかを 1 回だけ確認したい場合は、次の 2 つの解決策が考えられます (1 つは正規表現を使用し、もう 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

誰かが質問に答えましたが、削除しました ,

次の短いコードは正しく機能します。

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

正規表現自体の中で結果を反転する方法について何か考えはありますか?

于 2013-06-29T00:32:54.387 に答える