2013-06-28 44 views
3

我試圖找到一個字符串包含一個字的只有一個發生,負環視正則表達式 - 只有一個發生 - Java的

例如

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

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

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

多個foo的可在字符串中的任何地方發生,所以他們可以不連續,

我測試在Java中使用字符串foobarfoo以下正則表達式匹配,但它不工作,它返回

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

我知道這個話題看似重複,但我很驚訝,因爲當我使用這個表達式:(foo)(?!.*foo).*它的作品!

任何想法,爲什麼出現這種情況?

+0

第二個正則表達式匹配第一個輸入字符串,就是發生了什麼。儘管如此,它會返回第二個輸入示例的「false」。 – jlordo

+0

但通常字符串可能不會以'foo'開頭 –

+0

現在編輯問題,'foo'可能發生在字符串中的任何地方,另一個'foo'也可能發生 –

回答

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))* 
+0

所以,你的意思是沒有通用的方法來處理更長的模式?像'ljkashdflasdfkjhasdflkjhasdlfkjhasdlkfjhasdlfjk'你不能這樣做,對吧?請注意''foo'只是一個例子 –

+0

@ArianHosseinzadeh:你可以用你想要的字符串來做到這一點。所有你需要的是在第一個字母上分割字符串以動態地組成你的模式。 –

+0

請您詳細說明'++ |'是什麼?爲什麼不在任何地方使用'。*'? –

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 
0

有人回答了這個問題,但刪除了它,

下面的短代碼工作正常:

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

如何反轉內部結果的任何想法正則表達式本身?

+0

什麼是「{0}」?如果這就是你的想法,它並不妨礙與「foo」匹配。事實上,它基本上將第一種選擇變爲無操作。 '{0}'有合法用途,但這不是其中之一。至於反轉正則表達式,你可以把它包裝在一個負面的向前看,但我不推薦它:'^(?!(?:(。*?foo。*){0} |(。*?foo。* ){2,})$)。+ $' –

1

你的正則表達式的問題是,第一個.*最初消耗整個字符串,然後退後,直到找到一個地方,其餘的正則表達式可以匹配。這意味着,如果字符串中有多個foo,那麼您的正則表達式將始終匹配最後一個。而從這個位置來看,前瞻總是會成功的。

用於驗證的正則表達式必須比用於匹配的正確表達式更精確。您的正則表達式失敗,因爲.*可以匹配標記字符串'foo'。您需要積極防止在您嘗試匹配的匹配之前和之後匹配fooCasimir's answer顯示了一種方法;這裏的另一個:

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

這不是很有效的,但我認爲這是一個更容易閱讀。事實上,你很可能使用這個表達式:

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

這是一個偉大的交易更低效的,但一個完整的正則表達式的n00b可能會找出它做什麼。

最後,請注意,這些正則表達式中的任何一個 - 我的或卡西米爾的 - 都使用向後看。我知道這似乎是這份工作的完美工具,但沒有。事實上,後顧之憂永遠不應該成爲你達成的第一個工具。而不僅僅是在Java中。無論使用哪種正則表達式,使用正常方式匹配整個字符串幾乎總是比使用lookbehinds更容易。而且通常效率也更高。

1

使用兩種錨定查找aheads:

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

幾個關鍵點是有負前瞻檢查錨定到開始2分Foo的,重要的containes輸入的結束。

相關問題