2011-07-19 50 views
2

我有以下形式的字符串新鮮的眼光:需要爲Java正則表達式,是太貪婪

canonical_class_name[key1="value1",key2="value2",key3="value3",...] 

的目的是捕捉一組在canonical_class_name然後交替key =值組。目前它與測試字符串不匹配(在以下程序中,testString)。

必須至少有一個鍵/值對,但可能有很多這樣的對。

問題:目前正則表達式抓住正則類名,正確的第一個鍵,但它吞噬了一切,直到最後一個雙引號,我怎麼讓它抓住鍵值對懶?

這裏是正則表達式,下面的程序放在一起:

(\S+)\[\s*(\S+)\s*=\s*"(.*)"\s*(?:\s*,\s*(\S+)\s*=\s*"(.*)"\s*)*\] 

根據你的喜好,你可能會發現程序的版​​本更容易閱讀。

如果我的節目傳遞的字符串:

org.myobject[key1=\"value1\", key2=\"value2\", key3=\"value3\"] 

......這些都是我組得到:

Group1 contains: org.myobject<br/> 
Group2 contains: key1<br/> 
Group3 contains: value1", key2="value2", key3="value3<br/> 

還要說明一點,使用String.split()我可以簡化表達,但我我將這作爲一種學習體驗,以更好地理解我的正則表達式,所以我不想使用這樣的捷徑。

import java.util.ArrayList; 
import java.util.List; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

public class BasicORMParser { 
    String regex = 
      "canonicalName\\[ map (?: , map)*\\]" 
      .replace("canonicalName", "(\\S+)") 
      .replace("map", "key = \"value\"") 
      .replace("key", "(\\S+)") 
      .replace("value", "(.*)") 
      .replace(" ", "\\s*"); 

    List<String> getGroups(String ormString){ 
     List<String> values = new ArrayList(); 
     Pattern pattern = Pattern.compile(regex); 
     Matcher matcher = pattern.matcher(ormString); 
     if (matcher.matches() == false){ 
      String msg = String.format("String failed regex validiation. Required: %s , found: %s", regex, ormString); 
      throw new RuntimeException(msg); 
     } 
     if(matcher.groupCount() < 2){ 
      String msg = String.format("Did not find Class and at least one key value."); 
      throw new RuntimeException(msg); 
     } 
     for(int i = 1; i < matcher.groupCount(); i++){ 
      values.add(matcher.group(i)); 
     } 
     return values; 
    } 
} 

回答

4

你幾乎自己回答了這個問題:讓他們很懶。也就是說,使用懶惰(又名非貪心不願意)量詞。只需將每個(\S+)改爲(\S+?),並將每個(.*)改爲(.*?)即可。但如果是我,我會改變這些子表達式,所以他們永遠不會匹配太多,而不管貪婪。例如,您可以使用([^\s\[]+)作爲類名稱,使用([^\s=]+)作爲關鍵字,使用"([^"]*)"作爲值。

雖然我不認爲這會解決您的真正問題。一旦你找到了它,所以它正確匹配所有的鍵/值對,你會發現它只有捕獲第一對(組#2和#3)和最後一對(組#4和#5) 。這是因爲,每次重複(?:\s*,\s*(\S+)\s*=\s*"(.*)"\s*)*時,這兩個組會覆蓋它們的內容,並且在前一次迭代中捕獲的內容都會丟失。沒有辦法繞過它,這至少是兩步操作。例如,您可以將所有的鍵/值對作爲一個塊進行匹配,然後分出各個對。

還有一件事。此行:

if(matcher.groupCount() < 2){ 

...可能是沒有做你認爲它做的事。 groupCount()是Pattern對象的靜態屬性;它會告訴正則表達式中有多少個捕獲組。無論比賽成功還是失敗,groupCount()將始終返回相同的值 - 在本例中爲5。如果比賽成功,一些捕獲組可能爲空(表示他們沒有參加比賽),但總是會有五個。


編輯:我懷疑這是你試圖爲最初:

Pattern p = Pattern.compile(
    "(?:([^\\s\\[]+)\\[|\\G)([^\\s=]+)=\"([^\"]*)\"[,\\s]*"); 

String s = "org.myobject[key1=\"value1\", key2=\"value2\", key3=\"value3\"]"; 
Matcher m = p.matcher(s); 
while (m.find()) 
{ 
    if (m.group(1) != null) 
    { 
    System.out.printf("class : %s%n", m.group(1)); 
    } 
    System.out.printf("key : %s, value : %s%n", m.group(2), m.group(3)); 
} 

輸出:

class : org.myobject 
key : key1, value : value1 
key : key2, value : value2 
key : key3, value : value3 

理解正則表達式的關鍵是這一部分:(?:([^\s\[]+)\[|\G)。在第一次通過時,它匹配課程名稱和開放方括號。之後,\G接管,將下一場比賽定位到前一場比賽結束的位置。

+0

這很好。你關於真實問題的直覺和組數都是正確的。還有兩件事......我想避免字符串拆分,但似乎需要以簡單的方式處理逗號分隔的鍵值對列表。它是否正確?之前我在想這些組織會創建匹配集的列表。列表 = MagicRegexObject.match(「(\ S +?\ s)」,stringOfContent);如果還不清楚,我想說,如果有一些神奇的正則表達式對象可以匹配事物並返回匹配對象的列表,那麼我想這樣做... – Quaternion

+0

有沒有這樣的事情? – Quaternion

+1

你的意思是,像Python的're.findAll()'方法,或PHP的'preg_match_all()'?事實上,每種主要的支持正則表達式的語言都有相同的東西 - 除了Java。 : - /如果這是你正在談論的中間捕捉,那也不會發生。 Java和大多數語言/風格一樣,沒有辦法檢索它們。你只需要重寫正則表達式來匹配鍵/值對,就像我在編輯中一樣。 –

2

對於非貪婪匹配,在模式後追加?。例如.*?與可能的最少字符數匹配。