エンタープライズ:特集 2003/07/18 11:58:00 更新
C Magazine

[C Magazine連載]プログラムのレシピ(第1回)
デスクトップメモを作る (6/9)

C MAGAZINE 2002年9月号より転載

メモデータの保存と読み込み
 これで、メモを追加したり削除したり、あるいはメモの内容を書いたりできるようになりました。アプリケーションの外観はだいぶんできあがってきましたね。でもまだこの段階では、プログラムを終了するとメモの内容が消えてしまいます。そこで次は、メモの内容をファイルに保存する処理を作ることにしましょう。そこまで作れば、まだ荒削りですがメモアプリケーションとしての役割を果たすようになります。

 「データをどうやって保存するか?」というのは、すべてのアプリケーションに共通する重要な問題です。アプリケーションの性質に合ったデータの形式をよく考える必要があります。前回特集で扱ったグラフィックエディタではビットマップ形式を使いましたが、今回は自分でデータ形式を決める必要がありそうです。

 データ形式を考えるときのコツは、まず具体的なデータ例を作ってみることです。実際のアプリケーションを使わなくてもかまわないので、紙なりテキストエディタなりにデータの例を書いてみましょう。

 デスクトップメモの場合、1枚のメモの内容はFig.9のような文字列です。

Fig.9 1枚のメモの内容

6/30
プログラムのレシピ締め切り

 文字列は1行のこともあれば改行で区切られた複数行のこともあります。これを単純にテキストファイルに出力すれば、一応メモの内容を保存することはできます。

 また、実際にはメモが複数枚の場合もありえます。そこで、すべてのメモの内容を順に出力することにしましょう。この場合、メモとメモとの間の区切りをはっきりさせないといけません。そのためにはFig.10のように、メモの内容の前に行数を書いておけばいいですね。Fig.10のようにしておけば、それぞれのメモの内容が区別できます。この例では、メモの内容はそれぞれ2行、3行、2行です。

Fig.10 複数枚のメモを順に出力し、行数を付加した例

2
7/7
秋葉原へ。メモリ初期不良交換
3
デスクトップメモの改良:
表示フォントの調整
ソースコードの整理
2
6/30
プログラムのレシピ締め切り

 さらに、タブの絵柄名やウィンドウの位置、状態がアクティブかどうかといった情報も保存しておく必要があります。そこで実際のメモデータでは、

・タブの絵柄名
・ウィンドウの位置(X座標、Y座標)
・状態がアクティブかどうか
・内容の行数

という情報を「,」で区切って並べることにしました。Fig.11はデータの例です。

Fig.11 保存されたメモデータの例

パンダ,904,527,0,2
7/7
秋葉原へ。メモリ初期不良交換
ひぐ,153,826,0,3
デスクトップメモの改良:
表示フォントの調整
ソースコードの整理
ひぐ,481,823,1,2
6/30
プログラムのレシピ締め切り

 このようにデータ形式を決めたら、あとはこの例を参照しながら保存と読み込みの処理を作ればいいのです。どちらから作ってもかまいませんが、一般には保存処理のほうがやや簡単です。

●プログラム例

 List 8はメモデータを保存するプログラムです。List 8-(1)のようなループを使って、すべてのメモに関する内容を順番に出力します。List 8-(2)では、タブの絵柄名やウィンドウの位置といった情報を「,」で区切って出力します。List 8-(3)はメモのテキストを出力する処理です。List 8-(4)では、作成したメモデータをファイルに書き込みます。

List 8 メモデータのセーブ(UConsoleForm.cpp)

// メモデータのセーブ
void TConsoleForm::SaveMemos() {
   TMemoForm *memoForm;
   int i;
   TStringList *output=new TStringList();

   //========================================================= (1)
   // 全てのメモフォームについて順番に出力する
   for (i=0; i<MemoFormList->Count; i++) {
       memoForm=(TMemoForm*)MemoFormList->Items[i];

       //===================================================== (2)
       // タブ絵柄名、メモフォームの位置、
       // メモのアクティブ状態、テキストの行数
       // を,で区切って出力
       output->Add(
           AnsiString(((TTab*)TabList->
               Items[memoForm->TabIndex])->Name)+","+
           memoForm->Left+","+
           memoForm->Top+","+
           (int)memoForm->ActiveMemo+","+
           memoForm->Memo->Lines->Count);

       //===================================================== (3)
       // メモのテキストを出力
       output->AddStrings(memoForm->Memo->Lines);

   }

   //========================================================= (4)
   // ファイルに出力
   SetCurrentDir(ExtractFilePath(Application->ExeName));
   output->SaveToFile(MemoFileName);

   delete output;
}

 一方、List 9はメモデータを読み込むプログラムです。最初のList 9-(1)は「,」で区切られた値を読み込むためのサブルーチンです。保存側では情報を「,」で区切って出力するので、読み込み側には逆の処理を行うルーチンが必要になるわけです。

List 9 メモデータのロード(UConsoleForm.cpp)

//============================================================= (1)
// 「,」で区切られた文字列から要素を切り出す。
// メモデータのロードに使う
static AnsiString GetToken(AnsiString &s) {
   AnsiString ret;

   // ,が文字列に含まれる場合、
   // ,より前の文字列を戻り値とし、
   // 引数の文字列の,以前を削除する。
   // 例えば引数の文字列が"1,2,3"の場合、
   // 戻り値は"1"、引数の文字列は"2,3"となる。
   int pos=s.Pos(",");
   if (pos>=1) {
       ret=s.SubString(1,pos-1);
       s=s.SubString(pos+1,s.Length()-pos);
       return ret;
   }

   // ,が文字列に含まれない場合には、
   // 戻り値を引数の文字列全体とし、
   // 引数の文字列は空にする
   ret=s;
   s="";

   return ret;
}

//============================================================= (2)
// メモデータのロード
void TConsoleForm::LoadMemos() {
   TMemoForm *memo;
   TStringList *input=new TStringList();
   AnsiString s, t;
   int lineNum, memoLines, i;

   //========================================================= (3)
   // メモデータをロードする。
   // メモデータが無ければロードしない
   SetCurrentDir(ExtractFilePath(Application->ExeName));
   if (!FileExists(MemoFileName)) return;
   input->LoadFromFile(MemoFileName);

   // メモデータの各行を順番に読んでいく
   for (lineNum=0; lineNum<input->Count; lineNum++) {
       s=input->Strings[lineNum];
       try {

           // 新しいメモを作成する
           memo=NewMemo();

           //================================================= (4)
           // セーブのときとは逆に、
           // タブ絵柄名、メモフォームの位置、
           // メモのアクティブ状態、テキストの行数
           // を順番に読み込む
           t=GetToken(s);
           for (i=0; i<TabList->Count; i++) {
               if (((TTab*)TabList->Items[i])->Name==t) break;
           }
           if (i==TabList->Count) memo->TabIndex=0;
           else memo->TabIndex=i;
           memo->Left=GetToken(s).ToInt();
           memo->Top=GetToken(s).ToInt();
           memo->ActiveMemo=GetToken(s).ToInt();
           memoLines=GetToken(s).ToInt();

           // 行数情報を元に、テキストを読み込む
           for (i=0; i<memoLines && lineNum<input->Count; i++) {
               memo->Memo->Lines->Add(input->Strings[++lineNum]);
           }

           // 最後にメモを表示する
           memo->Show();

       } catch (...) {
           Application->MessageBoxA(
               (MemoFileName+"の"+
                   lineNum+"行目にエラーがあります").c_str(),
               "ファイル読み込みエラー",
               mbOK
           );
           Close();
       }
   }
   delete input;
}

 List 9-(2)からが、読み込み処理の本体です。まずList 9-(3)では、メモデータをファイルからメモリ上に読み込みます。続くList 9-(4)では、保存のときとは逆の手順でメモに関する各種の情報を取得します。そして得た情報をもとに、新しいメモを作成して表示します。

 List 8とList 9を比べると、List 8のほうがシンプルですね。一般にデータファイルを扱うときには、保存のほうが読み込みよりも簡単な処理になる傾向があります。したがって、保存処理をまず作成しておき、その処理を参考にしながら読み込み処理を作る方法がお勧めです。

[C Magazine連載]プログラムのレシピ(第1回)
(1)連載のはじめに
(2)どんな機能を作るか
(3)作成のポイント
(4)タスクトレイの操作
(5)メモの作成と表示
(6)メモデータの保存と読み込み
(7)タブ画像の選択
(8)日程管理機能
(9)まとめ
・実行ファイル/リソース一式(.lzh形式/1.32MB)
・リスト(.lzh形式/5.45KB)

関連リンク
▼C MAGAZINE
▼定期購読のご案内
▼バックナンバー一覧
▼バックナンバー販売協力店
▼ひぐぺん工房
▼「デスクトップマスコットを作ろう」紹介ページ
▼「デスクトップマスコットを作ろう」著者ページ

前のページ | 1 2 3 4 5 6 7 8 9 | 次のページ

[松浦健一郎(ひぐぺん工房),C MAGAZINE]