2016-01-19 6 views
7

Wygląda na to, że Files.newBufferedReader() jest bardziej rygorystyczna pod względem UTF-8 niż naiwna alternatywa.Różne wyniki odczytu pliku za pomocą Files.newBufferedReader() i konstruowanie czytelników bezpośrednio

Jeśli utworzyć plik za pomocą jednego bajta 128 --- tak, to nie jest poprawnym UTF-8 znaków --- to chętnie się czytać gdybym skonstruować BufferedReader na InputStreamReader na skutek Files.newInputStream(), ale z Files.newBufferedReader() Wyjątek jest zgłaszany.

Ten kod

try (
    InputStream in = Files.newInputStream(path); 
    Reader isReader = new InputStreamReader(in, "UTF-8"); 
    Reader reader = new BufferedReader(isReader); 
) { 
    System.out.println((char) reader.read()); 
} 

try (
    Reader reader = Files.newBufferedReader(path); 
) { 
    System.out.println((char) reader.read()); 
} 

ma ten wynik:

� 
Exception in thread "main" java.nio.charset.MalformedInputException: Input length = 1 
    at java.nio.charset.CoderResult.throwException(CoderResult.java:281) 
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:339) 
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) 
    at java.io.InputStreamReader.read(InputStreamReader.java:184) 
    at java.io.BufferedReader.fill(BufferedReader.java:161) 
    at java.io.BufferedReader.read(BufferedReader.java:182) 
    at TestUtf8.main(TestUtf8.java:28) 

Czy to udokumentowane? I czy można uzyskać łagodne zachowanie z Files.newBufferedReader()?

+1

Dziki stab w ciemności, ale próbowałeś określający kodowanie znaków w newBufferedReader zadzwonić? – JustinKSU

+2

@JustinKSU Nie powinien. Ta metoda jest [udokumentowana] (http://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#newBufferedReader-java.nio.file.Path-) jako użycie UTF- 8. – VGR

Odpowiedz

5

Różnica polega na tym, że CharsetDecoder używany do dekodowania UTF-8 jest skonstruowany w dwóch przypadkach.

Dla new InputStreamReader(in, "UTF-8") dekoder jest skonstruowany przy użyciu:

Charset cs = Charset.forName("UTF-8"); 

CharsetDecoder decoder = cs.newDecoder() 
      .onMalformedInput(CodingErrorAction.REPLACE) 
      .onUnmappableCharacter(CodingErrorAction.REPLACE); 

ta jest wyraźnie określając, że nieprawidłowe sekwencje są po prostu zastąpiony standardowym charakterze zastępczej.

Files.newBufferedReader(path) używa:

Charset cs = StandardCharsets.UTF_8; 

CharsetDecoder decoder = cs.newDecoder(); 

W tym przypadku onMalformedInput i onUnmappableCharacter nie są nazywane tak masz domyślną akcję, która ma rzucić wyjątek widzisz.

Nie ma sposobu, aby zmienić to, co robi Files.newBufferedReader. Nie widziałem niczego, co by to dokumentowało podczas przeglądania kodu.

5

Z tego, co wiem, nie jest ona nigdzie udokumentowana i nie jest możliwe, aby nowy BufferedReader zachowywał się łagodnie.

Należy to jednak udokumentować. W rzeczywistości brak dokumentacji na nim jest ważnym błędem Java, moim zdaniem, nawet jeśli zmieniona dokumentacja kończy się stwierdzeniem "nieprawidłowe sekwencje znaków powodują niezdefiniowane zachowanie".

Co więcej, skoro nie ma dokumentacji na ten temat, nie sądzę, że można bezpiecznie polegać na zachowaniu, które obserwujesz. Jest całkiem możliwe, że przy przyszłej wersji InputStreamReader będzie domyślnie używany wewnętrzny, ściśle określony CharsetDecoder.

Tak więc, aby zagwarantować pobłażliwe zachowanie, wziąłbym kodu krok dalej:

try (
    InputStream in = Files.newInputStream(path); 
    CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); 
    decoder.onMalformedInput(CodingErrorAction.REPLACE); 
    Reader isReader = new InputStreamReader(in, decoder); 
    Reader reader = new BufferedReader(isReader); 
) { 
    System.out.println((char) reader.read()); 
} 
Powiązane problemy