Streamによる入出力

Streamとは

テキストファイルのデータを読み込むとき、WindowsRuntimeではこのようなコードでデータを読み込むことができます。

FileIOのReadTextAsyncの1つのメソッドでファイルのすべてを読み込むことができるので非常に便利です。

しかし、このテキストファイルが非常に巨大だった場合どうなるでしょうか。

 

ReadTextAsyncは非同期メソッドですがいつまでも非同期読み込み処理が終わらず、巨大なデータのすべてが必要でない場合余計なデータまで読み込むためアプリのレスポンスが悪くなります。

ファイルの読み込みもそうですが、動画のネットワークデータ転送などの場合、すべてのデータを転送しないと動画を再生できないようなアプリケーションは非常に非効率的です。動画を現在転送できているデータ分だけ再生できるようにすべきです。

 

そのような問題を解決するために、WindowsRuntimeではStreamという概念を用意しています。(.Netでもあります)

Streamとはデータが流れてくる川のようなもので、データを徐々に読み取ったりデータの一部だけ切り取って読み取ることができたりします。

13

 

 

先ほども書いたように、ファイルの読み取りやネットワークデータ転送などのI/Oですべてのデータを転送しきってからじゃないと読み取れないのは非効率なのでStreamのインターフェースはさまざまな入出力で用意されています。

 

WindowsRuntimeのStream

WindowsRuntimeで用意されているStreamは以下の4つがあります。

  • IInputStream
  • IOutputStream
  • IRandomAccessStream
  • InMemoryRandomAccessStream

IInputStreamはストリームからメモリへの読み込みStreamを提供し、

IOutputStreamはメモリからストリームへの書き込みStreamを提供します。

IRandomAccessStreamは以下の継承関係の図のように、IInputStreamとIOutputStreamを継承、つまり読み込みと書き込みを提供します。

InMemoryRandomAccessStreamはメモリ内からメモリ内へのストリームに限定したStreamを提供します。

 

 

 

14

 

IInputStreamの実装を見てみます。

15

 

ReadAsyncメソッドのみを提供しています。

つまり、「何バイトを読み込む」ことしかできません。ReadAsyncを呼び出すたびにその数のバイトずつStreamから読み込むことができるので逐次的な読み込みを提供します。

 

IOutputStreamの実装を見てみます。

16

WriteAsyncとFlushAsyncの2つのメソッドを提供します。

WriteAsyncはそのバッファのデータ分だけStreamに逐次的に書き込みを行います。

FlushAsyncメソッドは少し特殊なメソッドでほとんど使用することがありません。

Windowsはストリームからデータを書き込むときに内部バッファにデータをキャッシュします。内部バッファがいっぱいになったときか、一定時間内部バッファにアクセスがなかった場合、内部バッファのデータを書き込みます。

つまり内部バッファがデータを保持しているときにシステムがダウンするとファイルにデータが書き込まれないわけです。

そこで重要なデータの場合、FlushAsyncメソッドを使用することで内部バッファのデータをファイルに即座に書き込みます。

 

しかし本当に重要なデータ以外でFlushAsyncメソッドを頻繁に使用するとアプリのパフォーマンスが低下してしまうためほとんどのアプリでFlushAsyncを使用する必要はありません。

 

IRandomAccessStreamの実装を見てみます。

IInputStreamとIOutputStreamはデータの逐次的な書き込みか読み込みを提供します。しかし「データのここからここまでを書き込みたい」とか、「データの最後何バイトだけ読み込みたい」みたいなことを実現することができません。

 

その機能を提供するのがIRandomAcccessStreamです。

IRandomAccessStreamはSeekメソッドを提供し、ストリームの好きな位置まで移動することができ、好きな位置からデータを読み込み、書き込みすることができます。まさにランダムアクセスが可能なわけです。

17

 

 

ストリームのサンプル

ではストリームによるファイル読み込みと書き込みの簡単なサンプルを見てみましょう。

“ABCDEFG”7文字のデータ、つまりASCIIエンコードで7バイトのデータをファイルに書き込みます。

StorageFileのOpenAsyncメソッドでFileAccessModeをReadWriteにすると、読み込みと書き込みどちらもできるストリームを取得することができます。

WindowsRuntimeでストリームに書き込むためにはIBuffer型にしなければいけないので変換し、データを書き込みます。

 

データサイズをみてみると、7バイトすべて書き込めていることがわかります。

18

 

 

では次にこのファイルをストリームで読み込んでみましょう。

IRandomAccessStreamのReadAsyncメソッドの第二引数に読み込むバイトサイズを指定します。

今回は7バイトすべてを読み込んで出力してみます。

 

“ABCDEFG”の7バイトすべてを読み込むことができました。

19

 

 

ではストリームの機能を生かして3バイトから6バイトまでの4バイトを読み込んでみましょう。

Seekメソッドを使用することで指定バイトまでカーソルをシークすることができます。

ReadAsyncでカーソルの位置から4バイト読み込みます。

つまり、3バイトから4バイトまで読み込むランダムアクセスによる読み込みです。

結果、CDEFの4バイトを読み込むことができました。

20

 

 

DataReaderとDataWriter

ストリームを使って読み込んだり書き込んだりするにしても、double型のデータとかstring型のデータとか、いちいちIBufferに変換したりするのは面倒です。

そこでストリームにintやdouble、stringなどの標準データ型を書き込みやすくするためにWindowsRuntimeではDataReaderとDataWriterを提供しています。

 

DataWriterは内部バッファという内部にデータを保持する領域を持っています。

Write**から始まるさまざまなメソッドがありますが、すべて内部バッファに書き込むために存在しています。

23

 

そしてStoreAsyncメソッドを実行することで初めてファイルに内部バッファからデータがストリームに書き込まれます。

21

 

 

DataReaderも同様に内部バッファを保持しています。

LoadAsyncメソッドはStreamから指定したバイトだけ内部バッファに読み込みます。

内部バッファに読み込んだデータは、Read**から始まるさまざまなメソッドによって標準型に変換することができます。

24

つまりLoadAsyncを実行したあと、Read**メソッドを実行することでデータを読み込むことができるわけです。

22