2017-05-26 189 views
4

我有一串文本行。
某些行的格式爲「key:value」。其他人應該被忽略。
我有一個固定的(預先定義的)我需要提取值並放入HashMap中的鍵列表。
所以,我在做這樣的事情:Java8:尋找解析「key:value」行文本的更好方法

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解析的更好的辦法?

+0

我強烈推薦https://github.com/jOOQ/jool它大大增強了流,使它們與LINQ一樣簡單。即使你不使用它,只要瀏覽可用的函數,就可以知道你可以用流做什麼。 – Novaterata

回答

3

根據你當前的問題陳述。你可以試試下面代碼..

  • 讀取一個文件,並在它外面
  • 創建流編譯每個字符串使用正則表達式
  • 過濾掉所有不與模式匹配字符串
  • 閱讀匹配的羣體地圖

你可能想改變它,按您的需求:

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))); 
} 

讓我知道,如果這不是你在找什麼

+0

我需要使用正則表達式而不是分割,因爲它並不總是正常工作。我怎樣才能做到這一點? – spoonboy

+0

@spoonboy我修改了我的答案現在它不使用拆分。它適用於所有場景。讓我知道它是怎麼跟你在一起的。 –

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

2

你當然可以使用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來實施最後一個贏取策略。

在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模式,以便^$分別匹配行的開始和結束。最後,像以前一樣,我們按允許的鍵過濾,然後收集到一個地圖。此示例顯示了最後一次獲勝合併函數作爲toMap的第三個參數。

+1

'findAll'是一個很好的補充! thx爲此 – Eugene