2016-01-19 27 views
7

Sembra che lo standard Files.newBufferedReader() sia più rigido rispetto a UTF-8 che l'alternativa ingenua.Diversi file di lettura dei risultati con Files.newBufferedReader() e costruzione diretta dei lettori

Se creo un file con un singolo byte 128 --- quindi, non è un carattere valido UTF-8 --- che verrà felicemente essere letto se io costruisco un BufferedReader su un InputStreamReader sul risultato di Files.newInputStream(), ma con Files.newBufferedReader() viene generata un'eccezione.

Questo codice

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

ha questo risultato:

� 
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) 

È questo documentato? Ed è possibile ottenere il comportamento clemente con Files.newBufferedReader()?

+1

pugnalata selvaggio nel buio, ma hai provato a specificare charset nel newBufferedReader chiamare? – JustinKSU

+2

@JustinKSU Non avrebbe dovuto. Questo metodo è [documentato] (http://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#newBufferedReader-java.nio.file.Path-) come se si utilizzasse UTF- 8. – VGR

risposta

5

La differenza sta nel modo in cui il CharsetDecoder utilizzato per la decodifica di UTF-8 è costruito nei due casi.

Per new InputStreamReader(in, "UTF-8") decodificatore è costruito utilizzando:

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

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

Questo è specificare esplicitamente che le sequenze non validi stanno solo sostituiti con il carattere di sostituzione standard.

Files.newBufferedReader(path) utilizza:

Charset cs = StandardCharsets.UTF_8; 

CharsetDecoder decoder = cs.newDecoder(); 

In questo caso onMalformedInput e onUnmappableCharacter non vengono chiamati in modo da ottenere l'azione di default, che è quello di gettare l'eccezione che state vedendo.

Non sembra essere un modo per modificare ciò che fa Files.newBufferedReader. Non ho visto nulla che documentasse questo mentre osservavo il codice.

5

Da quello che posso dire, non è documentato da nessuna parte, e non è possibile convincere il nuovo BufferedReader a comportarsi indulgentemente.

Tuttavia, dovrebbe essere documentato. In effetti, la mancanza di documentazione su di esso è un bug Java valido, secondo me, anche se la documentazione modificata finisce col dire che "sequenze di caratteri non validi generano un comportamento non definito".

Inoltre, poiché non c'è documentazione sull'argomento, non credo che si possa fare affidamento sul comportamento che si sta osservando. È del tutto possibile che una versione futura di InputStreamReader utilizzi di default un CharsetDecoder interno che sia rigoroso.

Così, per garantire un comportamento indulgente, vorrei prendere il codice di un passo più in là:

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