ファイルを操作してみよう - 1 / 3 -
第 6 回(連載参照)、第 7 回(連載参照)と描画に関する話題を詳しく解説してきました。描画については一通り解説しましたので、今回は第 6 回でも少しだけ取り上げたファイルシステムについて解説したいと思います。
ファイルシステムふたたび
第 6 回でも解説しましたが、 BREW には JAVA (J2ME) とは違い PC のようなファイルシステムが存在します。このファイルシステムはディレクトリの作成や階層化などもサポートしており、絶対ファイル名の最大長が 64 文字(エミュレータ上では 256 文字)などの制約はありますが便利な構造になっています。以下に BREW のファイルシステムの一例をもう一度取り上げておきます。
BREW のファイルシステムの例
BREW では、各アプリは自分のフォルダの中と共有ディレクトリのみアクセスすることができます。つまり、ほかのアプリのディレクトリを操作したり編集したりすることはできません。また、共有ディレクトリが操作できるかどうかは実装依存の部分があります。
BREW でファイル操作を行う場合には、 mif ファイルの特権レベルで、ファイル操作または全ての操作を許可する設定がなされていなければなりません。また、共有ディレクトリへのアクセスは、共有ディレクトリの操作または全ての操作を許可する設定がなされていなければなりません。
特権レベルの設定
ファイルシステムを操作してみる
BREW でディレクトリの操作やファイルの列挙、情報取得を行うには、 IFileMgr インタフェースを使用します。また、具体的なファイルの読み書きは IFile インタフェースを使用します。まず、 IFileMgr インタフェースを使用したファイルシステムの操作について解説したいと思います。
IFileMgr インタフェースには以下のような API が存在します。ファイルシステムの操作は、これらの API を使用して行います。
関数名 | 解説 |
---|---|
IFILEMGR_EnumInit | ディレクトリやファイルの列挙を行うためにインタフェースを初期化します。 |
IFILEMGR_EnumNext | 列挙中の次のディレクトリやファイルの情報を取得します。 |
IFILEMGR_EnumNextEx | 列挙中の次のディレクトリやファイルの拡張情報を取得します。 |
IFILEMGR_GetInfo | 名前で指定されたディレクトリやファイルの情報を取得します。 |
IFILEMGR_MkDir | ディレクトリを作成します。 |
IFILEMGR_RmDir | ディレクトリを削除します。 |
IFILEMGR_OpenFile | ファイルを開きます。 |
IFILEMGR_Remove | ファイルを削除します。 |
IFILEMGR_Test | 指定された名前のディレクトリやファイルが存在するか確認します。 |
IFILEMGR_Rename | ディレクトリやファイルの名前を変更します。 |
IFILEMGR_GetFreeSpace | ファイルシステムで使用可能な空きバイト数を取得します |
IFILEMGR_GetLastError | より詳しいエラー情報を取得します。 |
※上記表は、頻繁に使用される API のみです。より詳しい情報は、 BREW API リファレンスを参照してください。
ディレクトリを操作してみる
それでは上記 API を使用して、新しいディレクトリを作成した後、名前を変更してから削除してみましょう。まず、以下にコードを示します。
IShell* shell = app->a.m_pIShell; IFileMgr* filemgr; // IFileMgr インタフェースの作成 ISHELL_CreateInstance(shell,AEECLSID_FILEMGR,(void*)&filemgr); // ディレクトリの作成 IFILEMGR_MkDir(filemgr,"new_dir"); // 名前の変更 IFILEMGR_Rename(filemgr,"new_dir","temp_dir"); // ディレクトリの破棄 IFILEMGR_RmDir(filemgr,"temp_dir"); // IFileMgr インタフェースの破棄 IFILEMGR_Release(filemgr);
まず、 IShell インタフェースを使用して IFileMgr インタフェースのオブジェクトを作成します。 "new_dir" という名前のディレクトリを作成し、"temp_dir" という名前に変更した後、 "temp_dir" という名前のディレクトリを削除しています。ここで注意点は、 IFILEMGR_MkDir 関数は既に同名のディレクトリが存在する場合でも成功したことを示す値を返します。また IFILEMGR_RmDir 関数はディレクトリの中身が空の状態でないと正しく削除できません。よって、上記コードを実験する場合は、同名のディレクトリが存在しない環境で行う必要があります。
次に、深い階層のディレクトリを作成してみましょう。 IFILEMGR_MkDir 関数は存在しない親ディレクトリを再帰的に作成してはくれませんので、深い階層のディレクトリを作成するには親になる階層を逐一作成しなければなりません。
以下に 3 階層のディレクトリを作成するコードを示します。
IShell* shell = app->a.m_pIShell; IFileMgr* filemgr; // IFileMgr インタフェースの作成 ISHELL_CreateInstance(shell,AEECLSID_FILEMGR,(void*)&filemgr); // 1 階層目のディレクトリを作成 IFILEMGR_MkDir(filemgr,"1st_dir"); // 2 階層目のディレクトリを作成 IFILEMGR_MkDir(filemgr,"1st_dir/2nd_dir"); // 3 階層目のディレクトリを作成 IFILEMGR_MkDir(filemgr,"1st_dir/2nd_dir/3rd_dir"); // IFileMgr インタフェースの破棄 IFILEMGR_Release(filemgr);
上記のように、 BREW ではディレクトリやファイルのパスは絶対パスで指定します。カレントディレクトリなどの概念は存在しません。
ディレクトリやファイルを再帰的に列挙する方法
ファイルシステムの操作を行っていると、全てのディレクトリやファイルを再帰的に巡回して列挙したくなることがあります。また、 IFILEMGR_RmDir 関数は中身が完全に空でないと正常に削除できないため、再帰的な列挙はディレクトリの中身を空にすることにも利用できます。
static void GetInfoRecursive(FilesystemApplet* app,const char* name) { IShell* shell = app->a.m_pIShell; IFileMgr* filemgr; FileInfo info; // IFileMgr インタフェースの作成 ISHELL_CreateInstance(shell,AEECLSID_FILEMGR,(void*)&filemgr); // 列挙をディレクトリ用に初期化 IFILEMGR_EnumInit(filemgr,name,TRUE); // 次の要素を取得 while (IFILEMGR_EnumNext(filemgr,&info)) { // ディレクトリ名の出力 DBGPRINTF("Directory found : %s",info.szName); // ディレクトリの中を再帰的に巡回 GetInfoRecursive(app,info.szName); } // 列挙をファイル用に初期化 IFILEMGR_EnumInit(filemgr,name,FALSE); // 次の要素を取得 while (IFILEMGR_EnumNext(filemgr,&info)) { // ファイル名の出力 DBGPRINTF("File found : %s",info.szName); } // IFileMgr インタフェースの破棄 IFILEMGR_Release(filemgr); return; } // 呼び出し GetInfoRecursive(app,"");
上記コードはディレクトリを再帰的に巡回し、見つかったディレクトリやファイルをデバッグ出力に書き出します。最上位階層は "" で表現され、まず最上位階層から巡回を初めています。上記コードでは、ディレクトリごとに IFileMgr インタフェースを作成しているので少々動作が重いですが、一つの IFileMgr インタフェースを使いまわすとディレクトリごとに IFILEMGR_EnumInit 関数が呼び出されるので正確な列挙を行えません。
一つの解決策として、一つの IFileMgr インタフェースを使いまわして、あらかじめファイルシステムの状態をメモリに記憶しておいて巡回する方法があります。