2017-08-01 6 views
1

Java 8でファイルを読み込む際に奇妙な動作が発生しました。誰かがそれを理解できるかどうか疑問に思っています。不正な形式のファイルを読み取るときにStreamDecoderとInputStreamReaderを比較する

シナリオ:

不正な形式のテキストファイルを読む。間違った言い方をすれば、それはユニコード・コード・ポイントにマップされないバイトを含んでいることを意味します。次のように

私はそのようなファイルを作成するために使用するコードは次のとおりです。

byte[] text = new byte[1]; 
char k = (char) -60; 
text[0] = (byte) k; 
FileUtils.writeByteArrayToFile(new File("/tmp/malformed.log"), text); 

このコードは(も拡張1)ASCIIテーブルの一部ではありません、正確に1つのバイトを含むファイルを生成します。

UNICODE Replacement Characterです:catにこのファイルをしようと

は、次の出力を生成します。 UTF-8は非アスキー文字をデコードするために2バイト必要であるため、これは理にかなっていますが、我々は1つしか持っていません。これは私のJavaコードからも予想される動作です。いくつかの一般的なコードの貼り付け

:ここ

private void read(Reader reader) throws IOException { 

    CharBuffer buffer = CharBuffer.allocate(8910); 

    buffer.flip(); 

    // move existing data to the front of the buffer 
    buffer.compact(); 

    // pull in as much data as we can from the socket 
    int charsRead = reader.read(buffer); 

    // flip so the data can be consumed 
    buffer.flip(); 

    ByteBuffer encode = Charset.forName("UTF-8").encode(buffer); 
    byte[] body = new byte[encode.remaining()]; 
    encode.get(body); 

    System.out.println(new String(body)); 
} 

nioを使用して、私の最初のアプローチです:

FileInputStream inputStream = new FileInputStream(new File("/tmp/malformed.log")); 
read(Channels.newReader(inputStream.getChannel(), "UTF-8"); 

これは、次の例外を生成します。

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.Reader.read(Reader.java:100) 

私が期待したものではありませんどのこれは実際には腐敗しており、違法なfであるため、一種の意味がありますile、例外は基本的にもっと多くのバイトが読み込まれると予想しています。

そして、私の二番目の

(定期 java.ioを使用して):

FileInputStream inputStream = new FileInputStream(new File("/tmp/malformed.log")); 
read(new InputStreamReader(inputStream, "UTF-8")); 

これは失敗とcatとまったく同じ出力を生成しませんでした。また、理にかなっている

を。

だから私の質問は以下のとおりです。

  1. このシナリオでは、Javaアプリケーションからの予想される動作は何ですか?
  2. Channels.newReaderStreamDecoderを返す)と、通常のInputStreamReaderを使用するのに違いがあるのはなぜですか?私は何かを読んで間違って何かをしていますか?

説明があれば幸いです。

感謝:)

+0

あなたは 'InputStreamReader'に' UTF-8'を指定していないことに気づいていますか?あなたのプラットフォームのデフォルトのエンコーディングは 'UTF-8'なのでしょうか? 'InputStreamReader'は' StreamDecoder'も内部的に使用します。 – Kayaman

+0

"拡張されたもの":どの拡張? IBM437は、任意の順序で256バイトの値をすべて使用します。とにかく、テキストファイルが不正な形式になるとどう思いますか?あなたのアプリケーションについて、悪い入力を部分的に処理しなければならないことがありますか?アプリケーションがそれを拒否した場合、悪い入力をソースで修正することはできませんか?言い換えれば、MalformedInputExceptionは多くの場合に予想される動作です。 –

+0

@Kayamanありがとう、私はそれに気付かなかった。はい、私のプラットフォームのデフォルトはUTF-8です。私はCharsetを指定するようにコードを変更し、動作は変わりません。 (コードを編集しました) –

答えて

0

行動の違いは、実際には、右下StreamDecoder and Charset classesになります。InputStreamReader(すなわち、報告書の代わりに、さらにアップは例外で、その結果、交換してください)Channels.newReader(..)は、デフォルト設定で復号器を作成しているときにエラー

StreamDecoder(InputStream in, Object lock, Charset cs) { 
    this(in, lock, 
    cs.newDecoder() 
    .onMalformedInput(CodingErrorAction.REPLACE) 
    .onUnmappableCharacter(CodingErrorAction.REPLACE)); 
} 

に交換をしているStreamDecoder.forInputStreamReader(..)からCharsetDecoderを取得

public static Reader newReader(ReadableByteChannel ch, 
           String csName) { 
    checkNotNull(csName, "csName"); 
    return newReader(ch, Charset.forName(csName).newDecoder(), -1); 
} 

彼らは違った働きをしますが、その違いについてはドキュメンテーションには何の示唆もありません。これは間違って文書化されていますが、私はと思っています。

文字エンコーディングを扱うときは注意してください。

関連する問題