2014-09-26 34 views
7

Files.lines (...) ile çok büyük (500mb) bir dosya okuyorum. Bu dosyanın bir kısmını okur ancak bazı noktada java.io.UncheckedIOException ile kırar: java.nio.charset.MalformedInputException: Girdi uzunluğu 1Files.lines, Java8'de kırık satırları atlamak için

Ben dosya farklı chartsets'ten ile çizgileri olduğunu düşünüyorum =. Bu kırık çizgileri atlamanın bir yolu var mı? Geri dönen akışın bir Reader tarafından desteklendiğini ve okuyucunun nasıl atlanacağını bildiğimi biliyorum, ancak Reader'ı istediğim gibi ayarlamak için akıştan nasıl alacağımı bilmiyorum. Önceden dekoder zaten bir istisna ile çözme durduğunda

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 
    } 

cevap

9

Sen kod çözme sonrasında geçersiz karakterler ile satırları filtreleyemezsiniz. Geçersiz girdiyi yok saymasını ya da bu girişi özel bir karakterle değiştirmesini söylemek için el ile CharsetDecoder'u yapılandırmanız gerekir.

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

Bu, karakterleri atlayarak karakter kod çözme hatalarını görmezden gelir. hataları içeren tüm satırları atlamak için, dekoder hatalar için bir yedek karakteri ('\ufffd' varsayılan olarak) takın ve bu karakteri içeren satırları süzmek sağlayabilirsiniz: Bu durumda

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

. Ayrıca boilerplate kodundan kaçınmak için akıntıyla birlikte yapmanın mümkün olup olmadığını merak ediyordum, ama görünmüyor (akım bir Okuyucu tarafından destekleniyor, okuyucuyu bir şekilde almayı ve kod çözücüyü eklemeyi umuyordum) – Francesco

+2

Bu API tarafından desteklenmiyor. Ama olsa bile, buradaki koddan daha kompakt olmayacaktı. “Boilerplate” in sadece bir ek satır olduğunu unutmayın. Sonuç sadece daha fazla ayrıntıya neden olur çünkü yatay kaydırma yapmayı sevmediğimden daha cömert bir biçimlendirme yaptım. Peki, daha geniş kitlelere kod örnekleri gönderirken gerçek kodumdan daha fazla beyaz alan kullanıyorum. Elbette 'dec' ve' path' satırını inline edebilir ve 'StandardCharsets.UTF_8' ile' CodingErrorAction ''' '' Channels.newReader' '' FileChannel.open'' vb. Için 'import statik' kullanabilirsiniz. – Holger

+0

Evet katılıyorum. Tekrar teşekkürler Holger. – Francesco

0

, çözüm karmaşık olacak ve Akımlar API'sını kullanırken daha fazla hataya eğilimlidir. BufferedReader'dan okumak için normal bir for döngüsü kullanmamayı ve sonra MalformedInputException'ı yakalamayı öneririm. Bu aynı zamanda ayırt edilecek diğer IO istisnalarının yakalanmasını sağlar: Yanıtınız Holger için teşekkürler:

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

Yorumunuz için teşekkürler, bazı önemli noktalar var: * genellikle fonksiyonel stili kullanarak kod temizleyici ve daha özlü olun. Şu anda bir kaç kod satırı var ama eğer büyürse işlevsel * gitmek için bir yol olduğunu düşünüyorum. İstisnalar aracılığıyla programlama yapıyorsunuz, bu bir uygulama aslında ben * hoşunuza gitmiyor. Bu (kolay paralelleştirme, durum bilgisi olmayan kod, ...) bunu söyleyerek, çözümünüz kesinlikle başka bir çalışma çözümü. – Francesco

+0

@Fra iyi noktalar. Holger'ın cevabı daha ayrıntılı. Bununla birlikte, benimki de tembel olduğu için 'tembellik' kaybı yok. Ancak, Stream'in istisnaları ile başa çıkmak karmaşıktır. Hata ayıklamak zorunda kalmayıncaya kadar iyi görünüyor :) Ayrıca, Holgers çözümünde, hala bir IOException ile yakalamanız ve ele almanız gerekiyor. Bunu tamamen görmezden gelemezsen ve aktarırsan. En büyük sorun, Stream-filtre paradigması kullanılarak bu kodun çok yavaş olmasıdır. 500 MB'lik büyük bir dosya için filtrelerden kaçınırım. –