【问题标题】:Extract attributes of an string提取字符串的属性
【发布时间】:2015-06-03 21:35:45
【问题描述】:

我必须在这里处理一个由肮脏设计引起的问题。我得到一个字符串列表并想从中解析属性。不幸的是,我无法更改创建这些字符串的源。

例子:

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,其文本不受引号限制。此字段中也可能出现逗号,因此我不能使用字符串末尾的逗号来找出结束的位置。

另外,这些字符串并不总是包含所有属性。 typeinfodeactivated 始终存在,其余为可选。

有什么建议可以解决这个问题吗?

【问题讨论】:

  • 这些元素的顺序是固定的吗?
  • 如何搜索=,然后选择它前面的单个单词作为字段名称。 = 之后直到下一个字段名称的所有内容都是值。这假定该值不能包含 '=' - 如果可以,您就没有太多可做的了。
  • 如果所有 other 属性都有某种可预测的格式,您可以删除这些属性并将剩余的内容用于info...
  • 你觉得使用正则表达式怎么样?
  • @Stan OP 要求任何建议,所以我怀疑这是一个有效的选择。如果您有想法甚至准备好代码,请随时将其发布为答案。

标签: java string attributes extract


【解决方案1】:

一种可能的解决方案是在输入中搜索 = 字符,然后将其前面的单个单词作为字段名称 - 似乎您的所有字段名称都是单个单词(没有空格)。如果是这种情况,您可以将 = 之后的所有内容作为值。

这假定该值不能包含=

编辑:

作为处理嵌入 = 的一种可能方式,您可以查看它前面的单词是否是您已知的字段名称 - 如果不是,您可以将 = 视为嵌入字符而不是运算符.然而,这假设您有一组固定的已知字段(其中一些可能并不总是出现)。如果您知道字段名称区分大小写,则此假设可能会得到缓解。

【讨论】:

  • "这假设值不能包含=" 我们不需要这么强的假设。我们还可以假设=key=value 中的分隔符,前面只能有特定的单词集。如果它之前没有任何预定义的关键字,则它必须是值的一部分。
  • @Pshemo 嘿,我只是在打字 - 感谢您的评论。 :)
【解决方案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

EDIT: Version2 正则表达式搜索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

【讨论】:

  • 我的想法与您的版本 2 相似。但是您为什么不使用 keysStrRegex 作为实际密钥,即,而不是 \\w+
【解决方案3】:

您可以使用正则表达式,捕获所有“固定”组并使用 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 属性,还是三个,或者根本没有?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-09
    相关资源
    最近更新 更多