2014-09-26 30 views
7

我正在用Files.lines(...)讀取一個非常大(500mb)的文件。 它讀取的文件的一部分,但在某些時候它打破了java.io.UncheckedIOException:java.nio.charset.MalformedInputException:輸入長度= 1Files.lines跳過Java8中的折線

我認爲該文件具有不同的字符集的行。有沒有辦法跳過這些虛線?我知道返回的流是由讀者支持的,讀者我知道如何跳過,但不知道如何從流中獲取讀取器以根據需要設置它。

List<String> lines = new ArrayList<>(); 
    try (Stream<String> stream = Files.lines(Paths.get(getClass().getClassLoader().getResource("bigtest.txt").toURI()), Charset.forName("UTF-8"))) { 
     stream 
      .filter(s -> s.substring(0, 2).equalsIgnoreCase("aa")) 
      .forEach(lines::add); 
    } catch (final IOException e) { 
     // catch 
    } 

回答

9

當預配置的解碼器已經與異常停止解碼,無法篩選與無效字符解碼後線。您必須手動配置CharsetDecoder以指示它忽略無效輸入或用特殊字符替換該輸入。

CharsetDecoder dec=StandardCharsets.UTF_8.newDecoder() 
        .onMalformedInput(CodingErrorAction.IGNORE); 
Path path=Paths.get(getClass().getClassLoader().getResource("bigtest.txt").toURI()); 
List<String> lines; 
try(Reader r=Channels.newReader(FileChannel.open(path), dec, -1); 
    BufferedReader br=new BufferedReader(r)) { 
     lines=br.lines() 
       .filter(s -> s.regionMatches(true, 0, "aa", 0, 2)) 
       .collect(Collectors.toList()); 
} 

這簡單地忽略字符集解碼錯誤,跳過字符。要跳過包含錯誤的整個線路,可以讓解碼器插入替換字符(默認爲'\ufffd')的錯誤,並過濾掉包含字符行:

CharsetDecoder dec=StandardCharsets.UTF_8.newDecoder() 
        .onMalformedInput(CodingErrorAction.REPLACE); 
Path path=Paths.get(getClass().getClassLoader().getResource("bigtest.txt").toURI()); 
List<String> lines; 
try(Reader r=Channels.newReader(FileChannel.open(path), dec, -1); 
    BufferedReader br=new BufferedReader(r)) { 
     lines=br.lines() 
       .filter(s->!s.contains(dec.replacement())) 
       .filter(s -> s.regionMatches(true, 0, "aa", 0, 2)) 
       .collect(Collectors.toList()); 
} 
+0

感謝您的回答Holger。我也想知道是否可以用流來避免樣板代碼,但看起來是不可能的(該流由一個Reader支持,我希望能夠以某種方式獲取該閱讀器並添加解碼器) – Francesco 2014-09-27 07:40:05

+2

這不支持該API。但即使是這樣,它也不會比這裏的代碼更緊湊。請注意,「樣板」只是一條附加線。結果看起來更加冗長,只是因爲我不喜歡水平滾動,所以我做了更慷慨的格式化。那麼,當我發佈更廣泛的觀衆的代碼示例時,我比我在真實的代碼中使用更多的空白空間。當然,您可以爲'StandardCharsets.UTF_8'以及'CodingErrorAction。*','Channels.newReader','FileChannel.open'等內聯'dec'和'path'並使用'import static'。 – Holger 2014-09-27 08:20:59

+0

是的我同意。再次感謝Holger。 – Francesco 2014-09-27 09:00:32

0

在這種情況下,該解決方案將是複雜的,使用Streams API時更容易出錯。我建議只使用普通的for循環從BufferedReader中讀取數據,然後捕獲MalformedInputException。這也使捕獲其他IO異常可以區分:

List<String> lines = new ArrayList<>(); 

try (BufferedReader r = new BufferedReader(path,StandardCharsets.UTF_8)){ 
    try{ 
      String line = null; 
      while((line=r.readLine())!=null){ 
       if(line.substring(0, 2).equalsIgnoreCase("aa")){ 
        lines.add(line); 
       } 
    }catch(MalformedInputException mie){ 
      // ignore or do something 
    } 
} 
+0

感謝您的評論,我有幾點考慮: *通常使用功能樣式使代碼更清晰和更簡潔。目前有幾行代碼,但如果它增長,我認爲功能是要走的路線 *您是通過例​​外編程的,這是我不太喜歡的練習 *您失去了懶惰和所有的好處它(容易並行化,無狀態代碼,...) 有這樣的說法,你的解決方案絕對是另一種工作解決方案。 – Francesco 2014-09-30 09:41:50

+0

@Fra好點。 Holger的答案更加詳盡。然而,我的懶惰並沒有失去'懶惰',因爲它太懶惰了。但是,Stream的例外情況很複雜。看起來不錯,直到你必須調試:)另外,在Holgers解決方案中,你仍然需要捕獲和處理IOException。除非你會完全忽略它並傳遞它。最大的問題是使用Stream-filter範例,該代碼非常慢。對於500 MB的大文件,我會避免使用過濾器。 – 2014-10-01 08:34:08