MemoryStreamクラス
メモリへの読み書き
FileStreamクラスの項ではストリームを利用してファイルを読み書きする方法を紹介しましたが、ファイルとして保存する必要がない場合はMemoryStreamを利用します。
MemoryStreamはストレージ(HDDやSSDなど)ではなくメモリにデータを読み書きします。
Streamに関してはStreamを参照してください。
このページの解説は上記ページを読んでいることが前提となっています。
このページではusingステートメントを利用したコードで記述します。
using (MemoryStream ms = new MemoryStream())
{
//読み書き処理...
}
データの読み書き
ストリームへのデータの書き込みはWrite、WriteByteメソッドで行います。
ストリームからのデータの読み込みはRead、ReadByteメソッドで行います。
byte[] bytesNum1 = BitConverter.GetBytes(12345);
byte[] bytesStr1 = Encoding.Unicode.GetBytes("あいうえお");
byte b1 = 123;
byte[] bytesNum2 = BitConverter.GetBytes(0);
byte[] bytesStr2 = Encoding.Unicode.GetBytes("abcde");
byte b2 = 0;
using (MemoryStream ms = new MemoryStream())
{
//配列全体を書き込み
ms.Write(bytesNum1, 0, bytesNum1.Length);
//配列の2番目の要素から6つ分を書き込み
ms.Write(bytesStr1, 2, 6);
//1バイトを書き込み
ms.WriteByte(b1);
//ストリームの位置を先頭にセット
ms.Position = 0;
int read;
//配列の要素数分を読み込み
read = ms.Read(bytesNum2, 0, bytesNum2.Length);
//6バイトを読み取り配列の2番目の要素から格納
read = ms.Read(bytesStr2, 2, 6);
//1バイト読み取り
b2 = (byte)ms.ReadByte();
}
Console.WriteLine(BitConverter.ToInt32(bytesNum2, 0));
Console.WriteLine(Encoding.Unicode.GetString(bytesStr2));
Console.WriteLine(b2);
Console.ReadLine();
12345 aいうえe 123
Writeメソッドはbyte型配列をストリームに書き込みます。
第一引数はストリームに書き込む配列を指定します。
第二引数は配列のコピー開始位置を指定します。
第三引数は書き込むバイト数を指定します。
WriteByteメソッドは1バイトのデータをストリームに書き込みます。
Readメソッドは指定のバイト数をストリームから読み取り配列に格納します。
第一引数はデータを格納する配列を指定します。
第二引数は配列のコピー開始位置を指定します。
第三引数は読み取るバイト数を指定します。
戻り値は実際に読み取れたバイト数(int型)で、「0」が返ってくるとストリームの終端となります。
ReadByteメソッドは1バイトのデータをストリームから読み取り、int型で返します。
「-1」が返ってくるとストリームの終端となります。
これはFileStreamクラスと共通ですので詳しくはそちらを参照してください。
バイト型配列に変換
MemoryStreamはストリームのデータをToArrayメソッドでバイト型配列に変換できます。
byte[] bytesNum1 = BitConverter.GetBytes(12345);
byte[] bytesStr1 = Encoding.Unicode.GetBytes("あいうえお");
byte b1 = 123;
byte[] bytes;
using (MemoryStream ms = new MemoryStream())
{
ms.Write(bytesNum1, 0, bytesNum1.Length);
ms.Write(bytesStr1, 2, 6);
ms.WriteByte(b1);
//MemoryStreamをbyte型配列に変換
bytes = ms.ToArray();
}
File.WriteAllBytes("test.bin", bytes);
このサンプルコードはMemoryStreamの内容をbyte型配列に変換し、ファイルに書き出しています。
データのクリア
MemoryStreamに書き込んだデータを消去するにはSetLengthメソッドを使用します。
このメソッドは消去というよりはストリームのサイズを調整するものですが、引数に「0」を指定することで内容を消去できます。
using (MemoryStream ms = new MemoryStream())
{
ms.Write(new byte[] { 1, 2, 3 }, 0, 3);
foreach(var b in ms.ToArray())
Console.Write("{0} ", b);
Console.WriteLine();
ms.SetLength(0);
ms.Write(new byte[] { 7, 8, 9 }, 0, 3);
foreach (var b in ms.ToArray())
Console.Write("{0} ", b);
}
1 2 3 7 8 9
Capacityプロパティ
MemoryStreamは実際に保存しているデータのほかに、データ保存のためにあらかじめ確保してるメモリ領域があります。
メモリの確保はそこそこコストが掛かる処理なので、大きめにメモリ領域を確保することで高速化しています。
この領域のサイズはCapacityプロパティで取得/設定できます。
(実際のデータサイズはLengthプロパティで取得可能)
using (MemoryStream ms = new MemoryStream())
{
//何か処理...
//Lengthを0にセット
ms.SetLength(0);
//Capacityを128に設定
ms.Capacity = 128;
}
CapacityにLengthよりも小さい値を設定すると例外(ArgumentOutOfRangeException)が発生します。
コンストラクター
MemoryStreamのコンストラクターにはいくつかのオーバーロードがあります。
オーバーロード | 説明 | 書き込み | サイズ変更 |
---|---|---|---|
MemoryStream() | 空のストリームを生成 | ○ | ○ |
MemoryStream(int capacity) | 初期サイズを指定して空のストリームを生成 | ○ | ○ |
MemoryStream(byte[] buffer) | bufferに対するストリームを生成 | ○ | × |
MemoryStream(byte[] buffer, bool writable) | bufferに対するストリームを生成 | writableで指定 | × |
MemoryStream(byte[] buffer, int index, int count) | buffer[index]~buffer[index+count-1]までの範囲に対するストリームを生成 | ○ | × |
MemoryStream(byte[] buffer, int index, int count, bool writable) | buffer[index]~buffer[index+count-1]までの範囲に対するストリームを生成 | writableで指定 | × |
MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) | buffer[index]~buffer[index+count-1]までの範囲に対するストリームを生成 publiclyVisibleをtrueにするとGetBufferメソッドでバイト配列が得られる |
writableで指定 | × |
capacityはCapacityプロパティの初期サイズを指定してMemoryStreamを生成します。
第一引数にbyte型配列を指定するオーバーロードは、指定のbyte型配列を直接操作するMemoryStreamを生成します。
MemoryStreamでデータを変更すると元のbyte配列のデータも変更されます。
MemoryStreamのサイズは変更不可となります。
書き込みは可能ですが、writableをfalseに指定すると読み取り専用となります。
GetBuffer
最後のコンストラクターのpubliclyVisibleをtrueに指定すると、GetBufferメソッドが使用できます。
GetBufferメソッドは操作対象となっているbyte配列自体を返します。
static void Test(MemoryStream ms)
{
byte[] bytes = ms.GetBuffer();
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] *= 2;
}
}
static void Main(string[] args)
{
byte[] bytes1 = new byte[]
{ 1, 2, 3 };
using (MemoryStream ms = new MemoryStream
(bytes1, 0, bytes1.Length, true, true))
{
Test(ms);
byte[] bytes2 = ms.ToArray();
foreach (var b in bytes2)
Console.Write("{0} ", b);
}
Console.ReadLine();
}
2 4 6
ToArrayメソッドはMemoryStreamの内容をコピーしたbyte配列が返されますが、GetBufferメソッドは操作対象のbyte配列そのものを返します。
つまり戻り値のbyte配列のデータを変更するとMemoryStreamのデータも変更されます。
MemoryStream以外の方法でbyte配列を操作するような場合や、ToArrayメソッドのコピーに掛かるコストを省く場合に使用します。
publiclyVisibleがfalseになっているMemoryStreamに対してGetBufferメソッドを呼び出すと例外が発生します。
MemoryStreamのコピー
MemoryStreamを別のストリームにコピーするにはWriteToメソッドを使用します。
byte[] bytesNum = BitConverter.GetBytes(12345);
byte[] bytesStr = Encoding.Unicode.GetBytes("あいうえお");
byte b = 123;
string output = "test.bin";
using (MemoryStream ms = new MemoryStream())
using (FileStream fs = new FileStream(output, FileMode.Create))
{
ms.Write(bytesNum, 0, bytesNum1.Length);
ms.Write(bytesStr, 2, 6);
ms.WriteByte(b);
ms.WriteTo(fs);
}
Streamのコピー
MemoryStream以外のStreamを別のStreamにコピーする方法を紹介します。
.Net Framework4.0以降ではStreamクラスにCopyToメソッドが追加されているので、Streamのコピーはこれを使用するのが簡単です。
それ以前のバージョンでは以下のようなコードでコピーします。
/// <summary>
/// ストリームをコピーする。
/// </summary>
/// <param name="from">コピー元のストリーム</param>
/// <param name="to">コピー先のストリーム</param>
static void CopyStream(Stream from, Stream to)
{
if (!from.CanRead) return;
if (!to.CanWrite) return;
byte[] buf = new byte[65536];
while (true)
{
//読み取ったバイト数を取得
int read = from.Read(buf, 0, buf.Length);
if (read == 0)
break;
//読み取ったバイト数分を書き込み
to.Write(buf, 0, read);
}
}
static void Main(string[] args)
{
string input = "test.bin";
string output = "test_output.bin";
using (var fsIn = new FileStream(input, FileMode.Open))
using (var fsOut = new FileStream(output, FileMode.Create))
{
CopyStream(fsIn, fsOut);
}
Console.WriteLine("終了します。");
Console.ReadLine();
}