Streamクラス

Streamとは

C#では例えばファイルの読み書きなど、データの入出力の処理にストリーム(stream)という概念があります。
ストリームは「データの流れ」を意味するもので、ファイル以外にもメモリやネットワーク上のデータとのやり取りもストリームで扱うことができます。

ストリームの機能はStreamクラスから派生したクラスで提供されています。
Streamクラスは「System.IO」名前空間に定義されているので、コード先頭のusingディレクティブに追加しておきます。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

//↓追加しておく
using System.IO;

以下はStreamを扱う代表的なクラスです。

クラス名 説明
Stream データ入出力の基底クラス(抽象クラス)
以下のクラスはすべてStreamクラスの派生クラス
FileStream ファイルの入出力
MemoryStream メモリの入出力
NetworkStream ネットワークの入出力
BufferStream 他のストリームのデータの一時保存

Stream派生クラスはそれぞれで操作対象が異なりますが、基本的には同じように使用できます。
本来は扱い方が異なるものを、データの操作方法を共通化したものがStreamクラスです。
(それぞれの派生クラス独自の操作方法もあります)

Streamクラスは抽象クラスなので、それ自体のインスタンスは生成できません。
実際に使用するのはStreamクラスの派生クラスです。
このページではすべてのStream派生クラスで共通の操作方法を解説します。

オープンとクローズ

ストリームを使用するにはまずストリームをオープンします。
これはStream派生クラスのインスタンスを生成し、ストリームを使用可能な状態にします。

ストリームが不要になったらクローズします。
これはストリームで確保しているデータ(メモリやファイルなど)を解放する処理です。

オープンとクローズはセットで行います。
「ノートを開き、読み書きし、最後にノートを閉じる」という動作と同じことで、開かずに読み書きはできませんし、開きっぱなしはリソースの無駄使いとなります。

ストリームのオープンは基本的にnewキーワードで新しいインスタンスを生成します。
コンストラクターの指定はそれぞれのStream派生クラスによって異なります。
Streamの種類によってはnew以外のオープン方法も用意されています。

ストリームのクローズはCloseメソッドまたはDisposeメソッドで行います。
これらのメソッドに違いはありません。


//「test.bin」を開く
FileStream fs = new FileStream("test.bin", FileMode.Open);

//何か処理...

//fsを閉じる
fs.Close();

//↓で閉じてもOK
//fs.Dispose();

ストリームをクローズした後はそのストリームに対する読み書きはできなくなります。

ストリームのオープンとクローズはusingステートメントの使用が推奨されます。


using (FileStream fs = new FileStream("test.bin", FileMode.Open))
{
    //何か処理...
}

このように記述すると、usingのブロックを抜けると自動的にDisposeメソッドが呼び出されます。
詳しくはusingステートメントを参照してください。

Streamの操作

Streamクラスには基本的なメソッドが定義されており、Streamの派生クラスから利用することができます。
FileStreamやMemoryStreamなど、Streamの種類が違っても共通の方法で読み書きができるということです。
以下は共通して使用できるメソッド/プロパティです。

以下のメソッド/プロパティの具体的な使用例は次ページのFileStreamクラスで改めて説明します。

Read(byte[] buffer, int offset, int count) ストリームからcountバイトを読み取り、bufferのoffset番目から格納
戻り値は読み取ったバイト数
ReadByte() ストリームから1バイトを読み取って返す
Write(byte[] buffer, int offset, int count) bufferのoffset番目からcountバイトをストリームに書き込む
戻り値は書き込んだバイト数
WriteByte(byte value) ストリームにvalueを書き込み

ストリームの読み書きはbyte型、byte型配列で行います。
そのままでは使いづらいので、使いやすくするためのクラスも用意されています。

ストリームの長さと現在位置

ストリームには「長さ」と「現在読み書きしている位置」という概念があります。
「長さ」はファイルで言えばファイルサイズと同じ意味で、ストリーム全体の大きさです。
「現在の位置」はメモ帳などで文章を編集するときに表示される「キャレット」と同じようなものです。
テキストエディタのキャレット

テキストエディタが現在のキャレットの位置に文章を追加したり削除したりするのと同じで、ストリームも「現在の位置」を基準にしてデータを読み書きします。
ストリームを読み書きすると読み書きした分だけ自動的に位置が移動します。

これらは以下のメソッドおよびプロパティで取得/設定します。

Lengthプロパティ ストリームの長さを取得(読み取り専用)
Positionプロパティ ストリームの位置を取得/設定
(long型)
Seek(long offset, SeekOrigin loc) ストリームの位置をloc+offsetの位置にセット
戻り値はセットした新しい位置(long型)
SeekOrigin.Begin→先頭
SeekOrigin.Current→現在の位置
SeekOrigin.End→末尾

SeekメソッドのSeekOriginは列挙型で、「Begin」「Current」「End」の三つの値があります。
これらの位置を基準とした位置にoffsetの値を加算した位置にPositionをセットします。

ただし、これらはStreamの種類や開き方によって使用できない場合があります。

ストリームの状態

Streamの種類や開き方によっては読み取り/書き込み専用であったりSeekが出来なかったりします。
それらの状態は以下のプロパティで取得できます。
読み取り専用プロパティなので設定はできません。
戻り値はすべてbool型です。

CanReadプロパティ ストリームが読み取り可能か
CanWriteプロパティ ストリームが書き込み可能か
CanSeekプロパティ ストリームがSeek可能か
CanTimeoutプロパティ ストリームがタイムアウト可能か
ネットワーク通信などで用いる

ストリームのコピー

Stream自体を別のStreamにコピーするにはCopyToメソッドを使用します。
ただしこのメソッドは.NET FrameWork4.0以降に実装されたものなので、それ以前でコピーする方法はMemoryStreamクラスの項で説明します。


//「test.bin」のLengthは100とする
using (FileStream fs = new FileStream("test.bin", FileMode.Open))
using (MemoryStream ms = new MemoryStream())
{
    //fsのデータをmsにコピー
    fs.CopyTo(ms);

    fs.Position = 10;

    //fsのデータをmsにコピー
    fs.CopyTo(ms);
    
    Console.WriteLine(ms.Length);
}
190

コピーされるのはコピー元のPositonの位置以降、コピー先のストリームにすでにデータが存在する場合はそのPositionの位置から上書きとなります。
上のサンプルコードではmsのLengthは「100 + (100 - 10) = 190」ということになります。