web-dev-qa-db-ja.com

Stream.Read()対BinaryReader.Read()を使用してバイナリストリームを処理する

バイナリストリーム(_byte[]_配列)を使用する場合、BinaryReaderまたはBinaryWriterを使用する主なポイントは、メソッドを使用したスト​​リームからのプリミティブデータ型の読み取り/書き込みの簡素化ReadBoolean()など、エンコードを考慮します。それが全体の話ですか? _BinaryReader/BinaryWriter_を使用せずにStreamを直接操作する場合、固有の利点または欠点がありますか? Read()などのほとんどのメソッドは両方のクラスで同じように見えますが、私の推測では、それらは同じように動作します。

バイナリファイルを2つの異なる方法で処理する簡単な例を考えてみてください(編集:この方法は効果がなく、バッファーを使用できることを理解しています。これは単なるサンプルです)。

_// Using FileStream directly
using (FileStream stream = new FileStream("file.dat", FileMode.Open))
{
    // Read bytes from stream and interpret them as ints
    int value = 0;
    while ((value = stream.ReadByte()) != -1)
    {
        Console.WriteLine(value);
    }
}


// Using BinaryReader
using (BinaryReader reader = new BinaryReader(FileStream fs = new FileStream("file.dat", FileMode.Open)))
{
    // Read bytes and interpret them as ints
    byte value = 0;    
    while (reader.BaseStream.Position < reader.BaseStream.Length)
    {
        value = reader.ReadByte();
        Console.WriteLine(Convert.ToInt32(value));
    }
}
_

出力は同じになりますが、内部で何が起こっていますか(たとえば、OSの観点から)?一般的に言えば、どの実装を使用するかが重要ですか? _BinaryReader/BinaryWriter_を使用する目的はありますか?この特定の場合、MSDNはStream.ReadByte()に関して次のように述べています。

Streamのデフォルトの実装は、新しいシングルバイト配列を作成してからReadを呼び出します。これは正式には正しいですが、非効率的です。

GC.GetTotalMemory()を使用すると、この最初のアプローチは2番目のアプローチの2倍のスペースを割り当てるように見えますが、より一般的なStream.Read()メソッドが使用される場合はそうではありませんバッファを使用してチャンクで読み取る場合)。それでも、これらのメソッド/インターフェースは簡単に統合できるように思えます...

19
w128

いいえ、2つのアプローチの間に主な違いはありません。追加のReaderがバッファリングを追加するため、それらを混在させないでください。ただし、パフォーマンスの大きな違いを期待しないでください。実際のI/Oがすべてを支配しています。

そう、

  • 移動する(のみ)byte[]があるときにストリームを使用します。多くのストリーミングシナリオで一般的です。
  • 処理するデータの他の基本タイプ(単純なbyteを含む)がある場合は、BinaryWriterとBinaryReaderを使用します。それらの主な目的は、組み込みフレームワーク型をbyte[]に変換することです。
14
Henk Holterman

大きな違いの1つは、I/Oをバッファリングする方法です。ここかそこか数バイトだけを読み書きしている場合、_BinaryWriter/BinaryReader_はうまく機能します。しかし、MBのデータを読み取る必要がある場合、一度に1つのbyte、_Int32_などを読み取るのは少し遅くなります。代わりに、より大きなチャンクを読み取り、そこから解析することができます。

例:

_// Using FileStream directly with a buffer
using (FileStream stream = new FileStream("file.dat", FileMode.Open))
{
    // Read bytes from stream and interpret them as ints
    byte[] buffer = new byte[1024];
    int count;
    // Read from the IO stream fewer times.
    while((count = stream.Read(buffer, 0, buffer.Length)) > 0)
        for(int i=0; i<count; i++)
           Console.WriteLine(Convert.ToInt32(buffer[i]));
}
_

これは少し話題から外れています...しかし、私はそれを捨てます:あなたが非常に巧妙になりたいと思ったら...そして実際にパフォーマンスを向上させてください...各_Int32_を解析し、Buffer.BlockCopy()を使用して一度にすべてを実行できます。

もう一つの例:

_// Using FileStream directly with a buffer and BlockCopy
using (FileStream stream = new FileStream("file.dat", FileMode.Open))
{
    // Read bytes from stream and interpret them as ints
    byte[] buffer = new byte[1024];
    int[] intArray = new int[buffer.Length >> 2]; // Each int is 4 bytes
    int count;
    // Read from the IO stream fewer times.
    while((count = stream.Read(buffer, 0, buffer.Length)) > 0)
    {
       // Copy the bytes into the memory space of the Int32 array in one big swoop
       Buffer.BlockCopy(buffer, 0, intArray, count);

       for(int i=0; i<count; i+=4)
          Console.WriteLine(intArray[i]);
    }
}
_

この例について注意すべき点がいくつかあります。これは、Int32ごとに1バイトではなく4バイトを使用します...したがって、異なる結果が得られます。 Int32以外の他のデータ型に対してもこれを行うことができますが、多くの人はマーシャリングを念頭に置くべきだと主張します。 (考えたいことを提示したかっただけです...)

11
poy