目次→http://1studying.blogspot.jp/2017/07/c-magicknetmokuzi.html#kuw02
様々なストレージメディア(バッキングストア)に対してデータの読み書きを行う際、
ストリームを使用します。
(ストリームからバイナリデータをバイト配列で取得、書き出しが行えます。)
バッキングストア(backing store)とは…
「ストリーム」から見たストレージメディア(ファイル、ネットワーク、メモリ等)の事を、
「バッキングストア」と言います。
「ストリーム」はストレージメディアを「バッキングストア」として利用できます。
「バッキングストア」は必ずStreamクラスを実装しているので、
Streamクラスから継承されたメソッドを使用する事により、
プログラムからは同じ操作でバイト配列の読み書きを行えます。
ストリーム(Stream)とは…
「ストリーム」を使用して「バッキングストア」からバイト配列の読み書きを行います。
「ストリーム」には以下の様な物があります。
・「ファイル」のバイト単位読み書き
→「System.IO.FileStream」クラス
「ファイルデータ」を「バッキングストア」として使用する「ストリーム」です。
・「ネットワーク」を介したバイトの送受信
→「System.Net.Sockets.NetworkStream」クラス
「ネットワーク上データ」を「バッキングストア」として使用する「ストリーム」です。
・「メモリ配列」のバイト単位読み書き
→「System.IO.MemoryStream」クラス
「メモリー」を「バッキングストア」として使用する「ストリーム」です。
「System.IO.FileStream」クラスも
「System.Net.Sockets.NetworkStream」クラスも
「System.IO.MemoryStream」クラスも
「System.IO.Stream」クラスを継承している為、同じ使用方法で操作できます。
あと、特殊な「ストリーム」として以下の物があります。
・他の「ストリーム」に「バッファリング」を持たせる
→「System.IO.BufferedStream」クラス
主に「NetworkStream」とセットで使用されます。
(「FileStream」は内部でバッファリングされる為必要無し。
「MemoryStream」は読み書きが早い為、バッファリングの必要無し。)
・使用する際の注意
「System.IO.Stream」はあくまでバイトの読み書きを行う為の抽象クラスです。
抽象クラスの為、そのまま「new」して使う事はできません。
「Stream stream = new MemoryStream();」
のようにキャストして使用します。
あと注意点として、
テキストの読み書きによく使用される「StreamReader」や「StreamWriter」クラスは、
「Stream」の名前が入っているのですが、バイトの読み書きを行うのではなく、
エンコードを指定した文字の読み書きを行うクラスです。
「System.IO.Stream」を継承していない事に注意して下さい。
Streamクラス…
「System.IO.Stream」クラスについて説明します。
・プロパティ
「CanRead」→読み取り可能か
「CanWrite」→書き込み可能か
「Length」→ストリームの長さをbyte単位で取得
「CanSeek」→シークが可能かどうか。
ランダムアクセスも可 true
シーケンシャルアクセスのみしか出来ない false
「Position」→ストリームの現在位置を取得、設定する(ポインタ的なやつ)。
・メソッド
「ReadAsync」→非同期で読み取り。
「WriteAsync」→非同期で書き込み。
「Close」→ストリームを閉じ、リソース解放。(使わない。Disposeを使う。)
「Dispose」→ストリームを閉じ、リソース解放。
(Closeよりこちらを使うか、usingを使った方が良いです。)
「Flush」→ストリームにまだメディアに書き出されていないバッファがあれば、書き込み。
(ストリームを閉じる時にも裏側で呼ばれています)
「Read」→次の文字を読み取る。
「ReadByte」→1バイト読み取り。位置が末尾の場合-1を返す。
「Seek」→ストリームの現在位置を特定の位置へ移動。
「SetLength」→ストリームの長さをbyte単位で設定。
「Write」→指定した値をストリームに書き込む。
「WriteByte」→ストリームの現在位置に1バイト書き込み。
「CopyToAsync」→ストリームから非同期で読み取り、別のストリームに書き込む。
準備
試しに「ストリーム」を使ってみます。
その為の準備を行います。
「ファイル→新規作成→プロジェクト」
「テンプレート→VisualC#→Windowsクラシックデスクトップ→Windowsフォームアプリケーション」
を作成します。
↓
私はプロジェクトの名前を「testStream」としました。
次に、
「Form1」のデザイナへ
「button1」、「button2」、「pictureBox1」を配置します。
↓
配置したら、「button1」と「button2」をダブルクリックして、
「button1」と「button2」のクリックイベントのコードを表示させておきます。
private void button1_Click(object sender, EventArgs e) { } private void button2_Click(object sender, EventArgs e) { }
準備が出来ました。
使ってみる
実際に「ストリーム」を試します。
「System.IO.FileStream」と「System.IO.MemoryStream」を使ってみます。
・「MemoryStream」を使ってみる
150*150pixelの塗りつぶし「Bitmap画像」作成し、
「Bitmap画像」→「MemoryStream」→「byte[]」
へと変換させています。
その後、
「byte[]」→「MemoryStream」→「Bitmap画像」
へと変換を戻し、円を描画して表示させます。
ビットマップ画像を一度、バイト配列としてメモリ領域へ書き出し、
そのメモリ領域のバイト配列をビットマップ画像として読み込み
ビットマップ画像を表示させる。
処理的には遠回りした事を行っています。
「button1」のクリックイベントのコードを以下の形にします。
private void button1_Click(object sender, EventArgs e) { var myBitmap = new Bitmap(150, 150); using (Graphics g = Graphics.FromImage(myBitmap)) { g.FillRectangle(Brushes.Green, g.VisibleClipBounds); //塗りつぶし } //Bitmap画像→ストリームに保存 var ms = new System.IO.MemoryStream(); // 本来はusingを使います myBitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); //ストリーム→byte[]へ変換 byte[] byteArray = ms.ToArray(); ms.Dispose(); //ストリーム閉じる //~~~~〜〜〜〜 //byte[]→ストリームへ変換 var ms2 = new System.IO.MemoryStream(byteArray); // 本来はusingを使います //ストリーム→Bitmap画像へ変換 var myBitmap2 = new Bitmap(ms2); ms2.Dispose(); //ストリーム閉じる using (Graphics g = Graphics.FromImage(myBitmap2)) { g.DrawEllipse(new Pen(Color.Black, 5), 25, 25, 100, 100); //円描画 } pictureBox1.Image = myBitmap2; }↓「実行」して「button1」を押下。
このように表示されます。
今回、
「MemoryStream」を使い、メモリを「ストリーム」として扱いました。
「MemoryStream」を使用し、
「Bitmap画像」から「byte[]」への相互変換をおこないましたが、
大きな画像を扱う場合は「MemoryStream」分のメモリが余計に使用されます。
メモリの使用量が気になる場合は、
この後説明する「「FileStream」をつかってみる」のコード内で使用している
「ImageConverter」クラスの
「ConvertTo()」メソッドと「ConvertFrom()」メソッドを
使用しても良いかもしれません。
・「FileStream」を使ってみる
150*150pixelの塗りつぶし「Bitmap画像」作成し、
「Bitmap画像」→「byte[]」
へと変換後、
「FileStream」を使い「test.bmp」ファイルへ「byte[]」を保存しています。
その後、
「FileStream」を使い「test.bmp」ファイルから「byte[]」を読み込みます。
「byte[]」→「Bitmap画像」
へと変換を戻し、四角を描画して表示させます。
処理的には遠回りした事を行っています。
「button2」のクリックイベントのコードを以下の形にします。
private async void button2_Click(object sender, EventArgs e) { var myBitmap = new Bitmap(150, 150); using (Graphics g = Graphics.FromImage(myBitmap)) { g.FillRectangle(Brushes.LawnGreen, g.VisibleClipBounds); //塗りつぶし } //Bitmap画像→byte[]へ変換 var converter = new ImageConverter(); byte[] byteArray = (byte[])converter.ConvertTo(myBitmap, typeof(byte[])); //ファイルを新規作成(ファイルストリーム生成) using (var fs = new System.IO.FileStream(@"./test.bmp", System.IO.FileMode.Create)) { //byte[]→ストリームへ書き込み await fs.WriteAsync(byteArray, 0, byteArray.Length); } //~~~~〜〜〜〜 byte[] byteArray2; //ファイルを開く(ファイルストリーム生成) using (var fs = new System.IO.FileStream(@"./test.bmp", System.IO.FileMode.Open)) { byteArray2 = new byte[fs.Length]; //ストリーム→byte[]へ読み込み await fs.ReadAsync(byteArray2, 0, byteArray2.Length); } //byte[]→Bitmap画像へ変換 Bitmap myBitmap2 = (Bitmap)converter.ConvertFrom(byteArray2); using (Graphics g = Graphics.FromImage(myBitmap2)) { g.DrawRectangle(new Pen(Color.Black, 5), 25, 25, 100, 100); //四角描画 } pictureBox1.Image = myBitmap2; }↓「実行」して「button2」を押下。
このように表示されます。
プロジェクトフォルダの「/bin/Debug」フォルダ内に
緑色で塗りつぶされた画像の「test.bmp」ファイルが作成されます。
今回、
「FileStream」を説明するにあたり、
「byte[]」を経由したいが為にかなり回りくどいやり方をしました。
「FileStream」を使わないのであれば、
//ファイル内容を全てbyte[]へ読み込む byte[] byteArray = System.IO.File.ReadAllBytes(@"./test.bmp"); //byte[]を全てファイルへ書き込む System.IO.File.WriteAllBytes(@"./test.bmp", byteArray);の様に書いた方がスマートです。
また、
通常の「Bitmap画像」ファイルへの読み書きは、
//「Bitmap画像」読み込み var myBitmap = new Bitmap(@"./test.bmp"); //「Bitmap画像」書き出し myBitmap.Save(@"./test.bmp");のようにもっとシンプルに行うのが正しいです。
ファイルを「ストリーム」で「バイナリ」として呼びたい時のみ
// ファイルストリーム生成 var fs = new System.IO.FileStream("./test.bmp", System.IO.FileMode.Open); // 本来はusingを使います // バッファ用にbyte[]生成 byte[] byteArray = new byte[fs.Length]; // byte[](バッファ)へデータを読み込む await fs.ReadAsync(byteArray, 0, (int)fs.Length); // ファイルストリームを閉じる fs.Dispose(); // byte[](バッファ)からメモリストリーム生成 var ms = new System.IO.MemoryStream(byteArray); // 本来はusingを使います // メモリストリームからBitmap画像生成 var myBitmap = new Bitmap(ms); // メモリストリームを閉じる fs.Dispose();のような形をとります。
メモ
ストリーム→byte[]変換、3パターン
var ms = new System.IO.MemoryStream(); とした場合… //パターン1 byte[] byteArray = ms.GetBuffer(); //パターン2 byte[] byteArray = new byte[ms.Length]; await ms.ReadAsync(byteArray, 0, byteArray.Length); か ms.Read(byteArray, 0, byteArray.Length); //パターン3 byte[] byteArray = new byte[ms.Length]; await ms.ReadAsync(byteArray, 0, (int)ms.Length); か ms.Read(byteArray, 0, (int)ms.Length); //ストリームはusingするか、閉じ忘れないようにしましょう。 ms.Dispose();他に「メモリストリーム」はクリップボードの読み書きにもよく使われます。
他
以下のサイトを参考にしています。
http://heppoco-programer-life.dreamlog.jp/archives/1214566.html
0 件のコメント:
コメントを投稿