【问题标题】:Parse a String of key=value to a Map将 key=value 的字符串解析为 Map
【发布时间】:2018-12-19 15:39:53
【问题描述】:

我正在使用一个提供 XML 的 API,我需要从一个实际上是字符串的标签中获取地图。示例:

拥有

Billable=7200,Overtime=false,TransportCosts=20$

我需要

["Billable"="7200","Overtime=false","TransportCosts"="20$"]

问题是字符串是完全动态的,所以,它可以像

Overtime=true,TransportCosts=one, two, three
Overtime=true,TransportCosts=1= 1,two, three,Billable=7200

所以我不能只用逗号分隔,然后用等号分隔。 是否可以使用正则表达式将类似的字符串转换为地图?

到目前为止我的代码是:

private Map<String, String> getAttributes(String attributes) {
    final Map<String, String> attr = new HashMap<>();
    if (attributes.contains(",")) {
        final String[] pairs = attributes.split(",");
        for (String s : pairs) {
            if (s.contains("=")) {
                final String pair = s;
                final String[] keyValue = pair.split("=");
                attr.put(keyValue[0], keyValue[1]);
            }
        }
        return attr;
    }
    return attr;
}

提前谢谢你

【问题讨论】:

  • 我认为你可以先用=拆分然后再用,拆分。一些单元测试在这里会非常有用:)
  • 您给出的示例的预期结果是什么,到目前为止您尝试了什么?向我们展示您尝试过的示例代码及其给出的结果。
  • you 如何识别应该如何解析/拆分该字符串?你使用什么规则?
  • @Shar1er80 结果应该是 ["Overtime"="true","TransportCosts"="one, two, three"] ["Overtime"="true","TransportCosts"="1 = 1,2,3","Billable=7200"]
  • "所以我不能只用逗号分隔,然后用等号分隔。" 为什么?使用带限制的split 可以解决“TransportCosts=1=1”的问题

标签: java regex xml string hashmap


【解决方案1】:

我看到这段代码使用 Guava

import com.google.common.base.Splitter;


/**
 *  parse string 'prop1=val1; prop2=val2' to map
 */
 public static Map<String, String> parseMap(final String keyValueString) {
     if (StringUtils.isEmpty(keyValueString)) return Collections.emptyMap();

      return Splitter.on(";")
            .trimResults()
            .withKeyValueSeparator('=')
            .split(keyValueString);
}

注意,Idea 显示警告,因为 Splitter 带有注释 com.google.common.annotations.Beta 还不错,但在番石榴库版本更新期间可能需要一些工作。

【讨论】:

    【解决方案2】:

    替代的,恕我直言,更简单的正则表达式:([^,]+=[^=]+)(,|$)

    ([^,]+=[^=]+) → 分组:除逗号外的任何内容,后跟 1 个等号,后跟除等号之外的任何内容...
    (,|$) → ... 以逗号或结尾分隔-线

    测试:

    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("([^,]+=[^=]+)(,|$)");
    
        String test1 = "abc=def,jkl,nm=ghi,egrh=jh=22,kdfka,92,kjasd=908@0982";
        System.out.println("Test 1: "+test1);
        Matcher matcher = pattern.matcher(test1);
        while (matcher.find()) {
            System.out.println(matcher.group(1));
        }
        System.out.println();
        String test2 = "Overtime=true,TransportCosts=1= 1,two, three,Billable=7200";
        System.out.println("Test 2: "+test2);
        matcher = pattern.matcher(test2);
        while (matcher.find()) {
            System.out.println(matcher.group(1));
        }
    }
    

    输出:

    Test 1: abc=def,jkl,nm=ghi,egrh=jh=22,kdfka,92,kjasd=908@0982
    abc=def,jkl
    nm=ghi
    egrh=jh=22,kdfka,92
    kjasd=908@0982

    Test 2: Overtime=true,TransportCosts=1= 1,two, three,Billable=7200
    Overtime=true
    TransportCosts=1= 1,two, three
    Billable=7200

    【讨论】:

      【解决方案3】:

      你可以使用

      (\w+)=(.*?)(?=,\w+=|$)
      

      请参阅regex demo

      详情

      • (\w+) - 第 1 组:一个或多个单词字符
      • = - 等号
      • (.*?) - 第 2 组:除换行符之外的任何零个或多个字符,尽可能少
      • (?=,\w+=|$) - 正向前瞻,需要 ,,然后是 1+ 个单词字符,然后是 =,或紧邻当前位置右侧的字符串结尾。

      Java 代码:

      public static Map<String, String> getAttributes(String attributes) {
          Map<String, String> attr = new HashMap<>();
          Matcher m = Pattern.compile("(\\w+)=(.*?)(?=,\\w+=|$)").matcher(attributes);
          while (m.find()) {
              attr.put(m.group(1), m.group(2));
          }
          return attr;
      }
      

      Java test:

      String s = "Overtime=true,TransportCosts=1= 1,two, three,Billable=7200";
      Map<String,String> map = getAttributes(s);
      for (Map.Entry entry : map.entrySet()) {
          System.out.println(entry.getKey() + "=" + entry.getValue());
      }
      

      结果:

      Overtime=true
      Billable=7200
      TransportCosts=1= 1,two, three
      

      【讨论】:

      • @FilipeR。要为 Wiktor 的响应添加更多内容,可以使用 Wiktor 提供的模式使用 String.replaceAll(String pattern, String replacement) 来实现相同的目的。 s = s.replaceAll("(\\w+)=(.*?)(?=,\\w+=|$)", "\"$1\"=\"$2\""); $1 和 $2 是模式中的捕获组。
      【解决方案4】:

      我注意到的第一件事是,您提供的数据不容易识别分隔符,但似乎可以识别的是逗号后跟大写字母分隔每个字段。

      这允许一种方法将分隔符更改为使用String.replaceAll("(?&lt;=,)([A-Z])", ",$1") 的正则表达式可以轻松识别的内容。现在您将拥有一个分隔符,您可以识别 (,,) 并拆分数据以在需要的地方插入引号。

      类似:

      public class StackOverflow {
          public static void main(String[] args) {
              String [] data = {
                      "Overtime=true,TransportCosts=one, two, three",
                      "Overtime=true,TransportCosts=1= 1,two, three,Billable=7200"
              };
      
              for (int i = 0; i < data.length; i++) {
                  data[i] = data[i].replaceAll("(?<=,)([A-Z])", ",$1");
                  String[] pieces = data[i].split(",,");
                  for (int j = 0; j < pieces.length; j++) {
                      int equalIndex = pieces[j].indexOf("=");
                      StringBuilder sb = new StringBuilder(pieces[j]);
                      // Insert quotes around the = sign
                      sb.insert(equalIndex, "\"");
                      sb.insert(equalIndex + 2, "\"");
                      // Insert quotes at the beginning and end of the string
                      sb.insert(0, "\"");
                      sb.append("\"");
                      pieces[j] = sb.toString();              
                  }
      
                  // Join the pieces back together delimited by a comma
                  data[i] = String.join(",", pieces);
                  System.out.println(data[i]);
              }
          }
      }
      

      结果

      "Overtime"="true","TransportCosts"="one, two, three"
      "Overtime"="true","TransportCosts"="1= 1,two, three","Billable"="7200"
      

      【讨论】:

        猜你喜欢
        • 2015-07-08
        • 1970-01-01
        • 2014-02-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-08
        • 2016-12-14
        相关资源
        最近更新 更多