簡體   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