ホーム > デベロッパ > BREW プログラミング入門 > ファイルを操作してみよう > - 3 / 3 -

ファイルを操作してみよう - 3 / 3 -

ファイルからの読み込み

それでは、いよいよファイルからデータを読み込んでみましょう。 BREW でファイルを読み込む場合、ほとんどの場合即時に読み込む事ができますが、ファイルの読み込み自体はノンブロッキングであると記述されています。

ノンブロッキングとはファイルからの読み込みが完全に終わる前に次の処理に実行が移ってしまう方式を指します。また、ブロッキングはその逆でファイルから完全にデータが読み込まれるまで、実行が次に移ることはありません。 BREW API リファレンスには IFILE_Read はノンブロッキングであると記述されているのですが、実際のところはブロッキング動作を行っているようです。

このあたりは、 IAStream インタフェースを使用してアクセスするインタフェースがほかに存在した場合に、処理を抽象的に行えるように、インタフェースはノンブロッキングであると定義されているのかもしれません。 IFile インタフェースとして使用する分にはブロッキングであると考えていいようですが、確証はできていません。少なくとも BREW SDK に付属してくるサンプルは、ブロッキングとして記述されています。

まずは、ノンブロッキングでないと仮定した場合の読み込み方法を示します。以下にファイルをオープンしたのち、ファイルからデータを読み込みバッファに格納するコードを示します。

IShell*  shell = app->a.m_pIShell;
IFileMgr*  filemgr;
IFile*  file;
char  buffer[128];
int  size;
// IFileMgr インタフェースの作成
ISHELL_CreateInstance(shell,AEECLSID_FILEMGR,(void*)&filemgr);
// ファイルの新規オープン
file = IFILEMGR_OpenFile(filemgr,"sample.txt",_OFM_READ);
// ファイルからのデータの読み込み
size = IFILE_Read(file,buffer,sizeof(buffer) - 1);
// 読み込んだデータの出力
buffer[size] = '\0';
DBGPRINTF("read : %s",buffer);
// ファイルのクローズ
IFILE_Release(file);
// IFileMgr インタフェースの破棄
IFILEMGR_Release(filemgr);

※エラー処理はしていません。実際のコーディングではより強固なエラー処理をするべきです。

次に、ノンブロッキングであることを意識した読み込み方法を示します。以下のコードは上記のコードと全く同じ内容の処理を行うコードです。

// Filesystem アプレット構造体
typedef struct FilesystemApplet {
  AEEApplet  a;
  // 確保したインタフェースオブジェクトを保持するメンバ
  IFileMgr*  filemgr;
  IFile*  file;
  char  buffer[128];
} FilesystemApplet;


static void ReadCallback(FilesystemApplet* app)
{
  int  size;
  // ファイルからのデータの読み込み
  size = IFILE_Read(app->file,app->buffer,sizeof(app->buffer) - 1);
  // 結果の判別
  switch (size) {
    case AEE_STREAM_WOULDBLOCK:  // ブロッキング中
      // コールバックの登録
      //
      // AEE_STREAM_WOULDBLOCK は IAStream で定義されていますが、
      // IFile では発生しないようです。
      IFILE_Readable(app->file,(PFNNOTIFY)ReadCallback,app);
      break;
    case 0:  // 失敗
      // ファイルのクローズ
      IFILE_Release(app->file);
      // IFileMgr インタフェースの破棄
      IFILEMGR_Release(app->filemgr);
      break;
    default:  // その他
      if (size > 0) {
        // 読み込んだデータの出力
        app->buffer[size] = '\0';
        DBGPRINTF("read : %s",app->buffer);
        // ファイルのクローズ
        IFILE_Release(app->file);
        // IFileMgr インタフェースの破棄
        IFILEMGR_Release(app->filemgr);
      }
      break;
  }
  return;
}
// ここから、読み込み処理
IShell*  shell = app->a.m_pIShell;
// IFileMgr インタフェースの作成
ISHELL_CreateInstance(shell,AEECLSID_FILEMGR,(void*)&app->filemgr);
// ファイルの新規オープン
app->file = IFILEMGR_OpenFile(app->filemgr,"sample.txt",_OFM_READ);
// コールバックの登録
IFILE_Readable(app->file,(PFNNOTIFY)ReadCallback,app);

上記コードでは、実際の読み込みをコールバック関数内で行っています。 IFILE_Readable 関数を使用すると、ファイルからデータが実際に読み込めるタイミングになるまで読み込み処理をペンディングできます。

この方法は、ネットワークからのデータの読み込みにも応用できます。コールバック型になるため、インタフェースの破棄のタイミングの管理や途中でユーザの操作が行われる可能性があるなど、処理の流れの管理がややこしくなると言う欠点があります。しかし、ファイルからの読み込みに時間がかかる環境などでは CPU の実行をストップしないですむため、こちらの実装のほうが適している場合もあります。

実機にはウォッチドッグタイマというものが存在し、 CPU を長時間占領されつづけられたときに、致命的なエラーが発生したとみなして実機を再起動してしまいます。ファイルからの読み込みをブロッキングで行い、余りにも長い時間がかかってしまうと、このウォッチドッグタイマに捕まってしまう危険性があります。これを避けるためにもノンブロッキングで読み込むことには意味があります。以下にノンブロッキングでの読み込み処理の流れをフローチャートにまとめておきます。

ノンブロッキング処理の流れ
ノンブロッキング処理の流れ

ところで、上記コードは IFILE_Read 関数が一度で完全にデータを読み込めることを仮定しています。もし、 IFILE_Read 関数が希望したバイト数を読み込めなかった場合、さらに IFILE_Readable 関数を使用してコールバックを登録する必要があるかもしれません。

ファイルへの書き込み

次に、ファイルへの書き込みを行ってみましょう。読み込みの時とは違い、ファイルへの書き込みはノンブロッキングではありません。こちらの処理は簡単な内容になります。以下に、新規にファイルをオープンしたのち、バッファからファイルにデータを書き出すコードを示します。

IShell*  shell = app->a.m_pIShell;
IFileMgr*  filemgr;
IFile*  file;
char  buffer[] = "If you didn't know about the FRAMEWORK for BREW,"
          " the development of your products will be in the deadline.";
// IFileMgr インターフェースの作成
ISHELL_CreateInstance(shell,AEECLSID_FILEMGR,(void*)&filemgr);
// ファイルの新規オープン
file = IFILEMGR_OpenFile(filemgr,"sample.txt",_OFM_CREATE);
// ファイルへのデータの書き込み
IFILE_Write(file,buffer,sizeof(buffer) - 1);
// ファイルのクローズ
IFILE_Release(file);
// IFileMgr インターフェースの破棄
IFILEMGR_Release(filemgr);

※エラー処理はしていません。実際のコーディングではより強固なエラー処理をするべきです。

IFILE_Write 関数は書き込みが成功した場合、書き込んだバイト数が、また何らかの原因で失敗した場合、 0 を戻り値として返すようです。 BREW API リファレンスには確実に書き込みが一度で終わるとは記述されていませんので、書き込みたいデータのサイズが書き込まれたデータのサイズよりも大きい場合は、 IFILE_Write 関数をもう一度呼び出すなどの処理が必要かもしれません。いずれにしても、あまりにも大きいデータを書き込もうとして実機のウォッチドッグタイマに捕まってしまわないようにしなければなりません。

まとめ

今回はファイルシステムの解説をした後、ディレクトリの操作方法やファイルの操作方法について解説しました。 BREW には非常に柔軟なファイルシステムが存在し、いろいろと面白いことができそうです。また、実装依存ではありますが共有ディレクトリが使用できるなどアプリ間でファイルを共有する仕組みも用意されています。

現在の実機では、アプリフォルダの容量に限界があり大量にファイルを使用するようなことは事実上できないかもしれませんが、今後容量が増えて環境が強化されることを期待しています。