繁体   English   中英

Java8:寻找一种更好的解析“key:value”行文本的方法

[英]Java8: Looking for a better way of parsing a text of “key: value” lines

我有一串文字行。
一些行的格式为“key:value”。 其他人应该被忽视。
我有一个固定的(预定义的)键列表,我需要为HashMap提取值并放入HashMap。
所以,我正在做这样的事情:

BufferedReader reader = new BufferedReader(new StringReader(memoText));

reader.lines().forEach(line->{
    if(line.startsWith("prefix1")){
        // Some code is required here to get the value1
    }  
    else if(line.startsWith("prefix2")){
        // Some code is required here to get the value2
    }  
    ...
}

有没有更好的方法在Java 8中实现解析?

根据你当前的问题陈述。 您可以尝试下面的代码..

  • 读取文件并从中创建流
  • 使用正则表达式编译每个字符串
  • 过滤掉所有与模式不匹配的字符串
  • 将匹配组读取到Map

您可能希望根据需要进行更改:

import static java.util.stream.Collectors.toMap;
//skipped
Pattern pattern = Pattern.compile("([a-zA-Z]+)\\s*:\\s*(.*)");
try (Stream<String> stream = Files.lines(Paths.get("<PATH_TO_FILE>"))) {
    Map<String, String> results =
            stream.map(pattern::matcher)
                    .filter(Matcher::find)
                    .collect(toMap(a -> a.group(1), a -> a.group(2)));
}

让我知道,如果这不是你想要的

// define your fixed keys in a list
List<String> keys = Arrays.asList("key1", "key2");
reader.lines()
      // use filter instead of if-else
      .filter(line -> line.indexOf(":")>-1 && keys.contains(line.substring(0, line.indexOf(":"))))
      // collect in to a map
      .collect(Collectos.toMap(line -> {
          return line.substring(0, line.indexOf(":"));
      }, line -> {
          return line.substring(line.indexOf(":") + 1);
      }))

但是你必须确保每一行都有不同的密钥。 或者它将抛出java.lang.IllegalStateException: Duplicate key

你当然可以使用split来做到这一点,但对于这样的情况,我认为正则表达式更灵活。 另请注意,按照您的示例,这是从字符串解析,因此我省略了对BufferedReader异常处理和关闭。

这是一个Java 8版本:

static String memoText = "foo: fooValue\r\n" +
                         "otherKey: otherValue\r\n" +
                         "# something else like a comment line\r\n" +
                         "bar: barValue\r\n";

static Map<String, String> parseKeysValues(String memoText) {
    Pattern pattern = Pattern.compile("([a-zA-Z]+)\\s*:\\s*(.*)");
    Set<String> allowedKeys = new HashSet<>(Arrays.asList("foo", "bar"));
    return new BufferedReader(new StringReader(memoText)).lines()
        .map(pattern::matcher)
        .filter(Matcher::matches)
        .filter(m -> allowedKeys.contains(m.group(1)))
        .collect(Collectors.toMap(m -> m.group(1), m -> m.group(2)));
}

这个想法是,给定一个行流,将它们与包含键和值的组的模式匹配。 当然,你可以调整模式以匹配任何有效字符或键和值,修剪空格等。然后, filter(Matcher::matches)只允许成功匹配。 此时,正则表达式组1是键,组2是值,因此我们只能过滤允许的键,然后将结果放入Map中。

如果存在重复键,则会抛出异常。 要实现不同的策略,请将第三个参数添加到toMap ,以将新值与现有值合并。 例如,使用(a, b) -> b来实现last-one-wins策略。

在Java 9中,这将变得更简单:

static Map<String, String> parseKeysValues9(String memoText) {
    Set<String> allowedKeys = Set.of("foo", "bar");
    return new Scanner(memoText).findAll("(?m)^([a-zA-Z]+)\\s*:\\s*(.*)$")
        .filter(mr -> allowedKeys.contains(mr.group(1)))
        .collect(Collectors.toMap(mr -> mr.group(1), mr -> mr.group(2), (a, b) -> b));
}

在这里,我们使用新的Set.of静态工厂方法初始化允许的键集。 我们还使用Scanner而不是BufferedReader来解析输入。 新的findAll方法将生成一个MatchResult流,其中包含来自输​​入的所有匹配项。 一个小皱纹是我们必须修改模式来处理行结尾,因为我们不再逐行阅读。 默认情况下, ^$匹配整个输入的开头和结尾。 我们插入(?m)指令以启用MULTILINE模式,以便^$匹配行的开头和结尾。 最后,和以前一样,我们按允许的键过滤,然后收集到Map。 此示例将最后一次合并函数显示为toMap的第三个参数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM