2015-06-03 75 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属性,或者三个,或者根本没有?