2015-11-29 8 views
22

私は4000バイトの0をファイルtest.txtに書き込む次のコードを書いています。次に、同じファイルを一度に1000バイトのチャンクで読み込みます。ObjectInputStreamを使用して同時に1024バイトしか読み込めないのはなぜですか?

FileOutputStream output = new FileOutputStream("test.txt"); 
ObjectOutputStream stream = new ObjectOutputStream(output); 

byte[] bytes = new byte[4000]; 

stream.write(bytes); 
stream.close(); 

FileInputStream input = new FileInputStream("test.txt"); 
ObjectInputStream s = new ObjectInputStream(input); 


byte[] buffer = new byte[1000]; 
int read = s.read(buffer); 

while (read > 0) { 
    System.out.println("Read " + read); 
    read = s.read(buffer); 
} 

s.close(); 

私は1000バイトを4回読み込むことが予想されます。

Read 1000 
Read 1000 
Read 1000 
Read 1000 

しかし、実際に何が起こるかは、私はすべての1024バイト(もっと良い言葉がないために)、「一時停止」を取得するように見えるということです。

1024バイト以上を読み込もうとすると、1024バイトに制限されます。 1024バイト未満を読み込もうとすると、1024バイト目で一時停止する必要があります。

出力ファイルtest.txtを16進数で調べると、ファイルに0だけ書き込んだにもかかわらず、0以外のバイトのシーケンスが7A 00 00 04 00 1029バイト離れていることに気付きました。 Here is the output from my hex editor.(質問に答えるには時間がかかりすぎますか)

私の質問は次のとおりです。なぜ完全に0を書いたときに、これらの5バイトがファイルに表示されるのですか?これらの5バイトは、1024バイトごとに発生する一時停止と関係がありますか?なぜこれが必要ですか?

+2

'InputStream.read(byte [])'は、できるだけ多くを読み込むことを保証しません。あなたが記述している「短読」の動作は、ファイルベースの入力であっても完全に合法です。したがって、バッファ全体を読み込みたい場合は、 'DataInput.readFully(byte [])'を使います。 – Nayuki

+0

@NayukiMinase私は理解しています。しかし、なぜそれが1024バイトのマークで一時停止するのか説明してください。たとえば、最大1024バイトしか読み込めないのであれば、それは意味をなさないでしょう。結果は1024,1024,1024,928になります。しかし、なぜ最初の1000バイトを読み込むのか混乱しますが、それは次の24バイトだけを読み取ってから、再び続行することができます。これは完全に恣意的ですね?それとも理由がありますか? – Zsw

+0

私は分かりません。しかし、ObjectInputStreamとObjectOutputStreamのどちらのJavadocもバイト配列の書式について何も言わないので、私が言及した5バイトのシーケンスに困惑しています。 – Nayuki

答えて

17

オブジェクトストリームは1024バイトの内部バッファを使用し、プリミティブデータをBlock Dataマーカーが先頭にあるストリームのブロック内にそのサイズのチャンクで書き込みます。これは、0x7Aの後に32ビット長単語(または0x77の後に8ビットの長さの単語が続きます)。したがって、1024バイトまで読み取ることができます。

実際のところ、オブジェクトストリームを使用してバイトを読み書きするだけの理由があります。バッファリングされたストリームを使用します。バッファリングはあなたのコントロール下にあり、ストリームヘッダーとタイプコードを持つオブジェクトストリームとは異なり、偶然にもゼロのスペースオーバーヘッドがあります。

NBシリアル化されたデータはテキストではなく、.txtという名前のファイルに保存しないでください。

+1

なぜ、私がオブジェクトストリームを使用しているのかについては、オブジェクトと同じストリーム内のバイトをシリアル化しようとしていたので、もっとそうでした。 2つの異なるストリームを作成し、それらを分離する方がよいでしょうか?オブジェクトストリームヘッダーと競合しないでしょうか? – Zsw

+1

それはもちろんです。確かに同じストリームを使用する必要があります。オブジェクトがある場合は、オブジェクトストリームを使用する以外に選択肢はありません。 – EJP

4

私は、あなたが、あなたのリソースを閉じ扱うBufferedInputStreamBufferedOutputStreamでバッファリングを追加し、あなたのbyte[]をシリアル化するためwriteObjectreadObjectを使用するようにtry-with-resources Statementを使用することをお勧め。その後、何かのように、

try (OutputStream output = new BufferedOutputStream(// 
     new FileOutputStream("test.txt"), 8192); // 
     ObjectOutputStream stream = new ObjectOutputStream(output)) { 
    byte[] bytes = new byte[4000]; 

    stream.writeObject(bytes); 
} catch (IOException ioe) { 
    ioe.printStackTrace(); 
} 

try (InputStream input = new BufferedInputStream(// 
     new FileInputStream("test.txt"), 8192); // 
     ObjectInputStream s = new ObjectInputStream(input)) { 
    byte[] bytes = (byte[]) s.readObject(); 
} catch (IOException | ClassNotFoundException ioe) { 
    ioe.printStackTrace(); 
} 

ように読むための関与部分配列がある場合、あなたは長さを追加する必要があります。反対側にstream.writeInt(len);int len = stream.readInt();を使用することができます。

8

ObjectOutputStreamおよびObjectInputStreamは、オブジェクトのシリアル化に使用される特別なストリームです。

stream.write(bytes);を実行すると、通常のストリームとしてObjectOutputStreamを使用しようとしています.4000バイトの書き込みでは、バイト配列オブジェクトの書き込みではありません。このようにデータがObjectOutputStreamに書き込まれると、特別に処理されます。documentation of ObjectOutputStreamから

:直列化可能フィールドおよび外部化データを除く

(強調鉱山)

プリミティブデータは、ブロックデータレコード内にObjectOutputStreamに書き込まれます。ブロックデータレコードは、ヘッダとデータで構成されます。ブロックデータヘッダーは、マーカーとヘッダーに続くバイト数で構成されます。連続したプリミティブデータの書き込みは、1つのブロックデータレコードにマージされます。 ブロック・データ・レコードに使用されるブロック係数は、1024バイトのです。各ブロック・データ・レコードは1024バイトまで充填されるか、またはブロック・データ・モードが終了するたびに書き込まれます。

この動作を期待しているのはなぜですか。

私は、代わりにwrite()writeObject()を使用し、あなたが本当にObjectOutputStreamを使用したい場合は、BufferedOutputStream代わりのObjectOutputStream、またはどちらかを使用することをお勧めします。対応は入力に適用されます。

関連する問題