2015-06-03 76 views
5

我得在這裏處理一個由髒設計引起的問題。我得到了一個字符串列表,並想從中解析出屬性。不幸的是,我無法改變這些字符串創建的來源。提取字符串的屬性

例子:

String s = "type=INFO, languageCode=EN-GB, url=http://www.stackoverflow.com, ref=1, info=Text, that may contain all kind of chars., deactivated=false" 

現在我想提取屬性typelanguageCodeurlrefinfodeactivated

這裏的問題是字段info,其文本不受引號的限制。在這個字段中也可能會出現逗號,所以我不能在字符串末尾使用逗號來查找結束位置。

另外,這些字符串並不總是包含所有屬性。 type,infodeactivated總是存在,其餘是可選的。

任何建議如何我可以解決這個問題?

+0

是這個要素的順序固定的嗎? – Pshemo

+1

如何搜索'=',然後選擇前面的單個字作爲字段名稱。在'='之後的所有內容,直到下一個字段名稱是值。這個假設值不能包含'=' - 如果可以的話,你沒有太多可選的東西。 – xxbbcc

+2

如果所有的_other_屬性都有一個可預測的格式,那麼可以刪除這些屬性,併爲'info'保留所有內容...... –

回答

2

假設元素的順序是固定的,你可以使用正則表達式像這樣的

String s = "type=INFO, languageCode=EN-GB, url=http://www.stackoverflow.com, ref=1, info=Text, that may contain all kind of chars., deactivated=false"; 

String regex = //type, info and deactivated are always present 
      "type=(?<type>.*?)" 
     + "(?:, languageCode=(?<languageCode>.*?))?"//optional group 
     + "(?:, url=(?<url>.*?))?"//optional group 
     + "(?:, ref=(?<rel>.*?))?"//optional group 
     + ", info=(?<info>.*?)" 
     + ", deactivated=(?<deactivated>.*?)"; 
Pattern p = Pattern.compile(regex); 
Matcher m = p.matcher(s); 
if(m.matches()){ 
    System.out.println("type -> "+m.group("type")); 
    System.out.println("languageCode -> "+m.group("languageCode")); 
    System.out.println("url -> "+m.group("url")); 
    System.out.println("rel -> "+m.group("rel")); 
    System.out.println("info -> "+m.group("info")); 
    System.out.println("deactivated -> "+m.group("deactivated")); 
} 

輸出寫溶液:

type -> INFO 
languageCode -> EN-GB 
url -> http://www.stackoverflow.com 
rel -> 1 
info -> Text, that may contain all kind of chars. 
deactivated -> false 

編輯:版本2正則表達式搜索oneOfPossibleKeys=value其中value結尾爲:

  • , oneOfPossibleKeys=
  • 或在其後面有字符串尾(由$表示)。

代碼:

String s = "type=INFO, languageCode=EN-GB, url=http://www.stackoverflow.com, ref=1, info=Text, that may contain all kind of chars., deactivated=false"; 

String[] possibleKeys = {"type","languageCode","url","ref","info","deactivated"}; 
String keysStrRegex = String.join("|", possibleKeys); 
//above will contain type|languageCode|url|ref|info|deactivated 

String regex = "(?<key>\\b(?:"+keysStrRegex+")\\b)=(?<value>.*?(?=, (?:"+keysStrRegex+")=|$))"; 
    // (?<key>\b(?:type|languageCode|url|ref|info|deactivated)\b) 
    // = 
    // (?<value>.*?(?=, (?:type|languageCode|url|ref|info|deactivated)=|$))System.out.println(regex); 

Pattern p = Pattern.compile(regex); 
Matcher m = p.matcher(s); 


while(m.find()){ 
    System.out.println(m.group("key")+" -> "+m.group("value")); 
} 

輸出:

type -> INFO 
languageCode -> EN-GB 
url -> http://www.stackoverflow.com 
ref -> 1 
info -> Text, that may contain all kind of chars. 
deactivated -> false 
+0

我有一個和你的版本2類似的想法。但是爲什麼你不用'keysStrRegex'作爲實際的密鑰,也就是說,而不是'\\ w +'? –

+0

@tobias_k這是一個非常好的問題。答案已更新。 – Pshemo

4

一種可能的解決方案是在輸入中搜索=字符,然後將緊接在它之前的單個字詞作爲字段名稱 - 似乎所有字段名稱都是單個字詞(沒有空格)。如果是這種情況,則可以將=之後的所有內容都作爲值分配給下一個字段名稱(分隔爲,)。

這裏假設該值不能包含=

編輯:

,作爲一種可能的方法來處理嵌入式=,你可以看到,如果在它前面的字是一個你已知的字段名稱 - 如果不是,你都不可能治療=作爲嵌入式字符而不是操作員。然而,這是假定你有一組固定的已知字段(其中一些可能並不總是出現)。如果您知道字段名稱區分大小寫,則可以減輕此假設。

+2

「這個假設值不能包含'='」我們不需要這麼強的假設。我們還可以假定在'key = value'中作爲分隔符的'='可以僅由特定的一組詞組開頭。如果它之前沒有任何預定義關鍵字,則它必須是值的一部分。 – Pshemo

+0

@Pshemo嘿,我只是打字 - 謝謝你的評論。 :) – xxbbcc

1

您可以使用正則表達式,捕獲所有「固定」組並使用任何剩餘的info。如果info部分包含,=字符,則這應該甚至可以工作。這裏有一個簡單的例子(使用Python,但這不應該是一個問題...)。

>>> p = r"(type=[A-Z]+), (languageCode=[-A-Z]+), (url=[^,]+), (ref=\d), (info=.+?), (deactivated=(?:true|false))" 
>>> s = "type=INFO, languageCode=EN-GB, url=http://www.stackoverflow.com, ref=1, info=Text, that may contain all kind of chars, even deactivated=true., deactivated=false" 
>>> re.search(p, s).groups() 
('type=INFO', 
'languageCode=EN-GB', 
'url=http://www.stackoverflow.com', 
'ref=1', 
'info=Text, that may contain all kind of chars, even deactivated=true.', 
'deactivated=false') 

是否有這些元素都是可選的,你可以把一個?這些團體後,使逗號可選。如果訂單可能不同,那麼它更復雜。在這種情況下,不要使用一個RegEx一次捕獲所有內容,而要使用多個RegEx捕獲各個屬性,然後在匹配下一個屬性之前刪除(替換爲'')字符串中的那些屬性。最後,匹配info


在進一步考慮,因爲這些屬性可以有任何命令,它可能是更有希望捕捉到剛剛一切從一個關鍵字跨越到下一個,而不管其實際內容的,非常類似於Pshemo的解決方案:

keys = "type|languageCode|url|ref|info|deactivated" 
p = r"({0})=(.+?)(?=\, (?:{0})=|$)".format(keys) 
matches = re.findall(p, s) 

但是,這也可能會在一些非常模糊的情況下失敗,例如如果info屬性包含類似', ref=foo'的內容,包括逗號。但是,似乎沒有辦法解決這些模糊問題。如果你有像info=in this string, ref=1, and in another, ref=2, ref=1這樣的字符串,它是否包含一個ref屬性,或者三個,或者根本沒有?