プロが教えるわが家の防犯対策術!

VC++6.0、Win32 APIを用いて作成しているダイアログボックスを表示するアプリについてです。

パターン(パターン1~3までの3種類)が格納される列と、文字列(最大256バイト)が
格納される列の2列からなるリストビューがあります。

そのリストビューに新しい情報(行)を追加するたびに、その該当行の情報を
別ファイル(設定ファイル)の構造体に格納し、リストビューの変更・削除があれば、
その都度構造体を更新し(?)反映させたいです。
メモリも無駄なく、効率的に使いたいです。

また次回以降そのアプリを起動させる際には、設定ファイルの内容を読み込んでリストビュー
の表示に反映させたいです。

正直、構造体は苦手なため解らないことだらけなのですが、パターンを格納するint型の変数と
文字列を格納する固定長256byteのchar型配列の変数が必要なのではと思っています。
後は、レコード数を格納するヘッダーも必要なのでしょうか・・・
メモリの取得、開放、移動方法はいまいち分かりません。

現在は、ラジオボタン(パターン用)とエディットボックス(文字列用)を用いて、
リストビューに登録するところまでは出来ています。
変更や削除の機能も実装出来ています。

上記の処理を実現させるための詳細な流れを教えてください。
サンプルコードも載せて頂けると幸いです。
分かりにくい文章で申し訳ありませんが、よろしくお願い致します。

A 回答 (16件中1~10件)

失礼しました、訂正です。


(誤)
また、文字列メモリは保持のため
必ずアプリケーション終了時に行います。
(正)
また、文字列メモリの解放は保持のため
必ずアプリケーション終了時に行います。

あとジャーナルデータは例えば個数を決めておきます
(個数=ロールバック回数+ロールフォワード回数)

N=記録可能最大個数(回数) M=現在回数
&Data[i]=該当回数目ローデータとした時、
回数 M が N を超えた時に、
 for (i=0;i<N-1;i++) Data[i]←Data[i+1]の処理;
 Data[N-1]←最新データ
とすればいくはずです。
あくまで考え方なので…(大汗)
    • good
    • 0

>ところで、構造体を使わないことによるデメリットは、ソートを行え


>ない以外に何かありますでしょうか。

リストビュー上で昇順・降順ソートやドラッグソート
(クリックで1項目を移動させるもの)
を行ってどんな順列になろうとも
書き込み前に先頭から書き出せば関係ありません。

しかし“ソート履歴”や“履歴リコール機能”
を実装する際は、単純にLV_ITEM 構造体を個数分用意して
インデックスの状態をジャーナルデータとして記録すれば
あの関数も、この構造体も…の必要ありません。

ジャーナルデータの構造は、シンプルです。
アプリケーション起動後から終了までの間...
(変更回数 / アクションフラグ=0:追加 1:入れ替え 2:変更 3:削除 /
対照インデックス / パターン / 文字列)
※パターン、文字列は変更の時だけ記録、以外は空記録します。
また、文字列メモリは保持のため
必ずアプリケーション終了時に行います。

1/0/0 … インデックス0を追加
2/0/1 … インデックス1を追加
3/1/1,0 … インデックス1と0入れ替え)
4/3/1 … インデックス1を削除
・・・
※ソートは1項目ずつ変更を記録します。
無駄に多くなるように思えますが、どっちみち最大数と同じ個数
ですから、支障はないです。

現在N回目とした場合、“M回前の状態に戻す”(M>0)なら
A/B/C
・N ポイントから N-M+1 ポイントまで以下を繰り返す
・もし B が...
0:C のインデックスを削除(切り詰め)
1:C の2つのインデックスを入れ替え
2:C のインデックスのパターンと文字列をコピー
3:C のインデックスを再追加
・・・
と言った感じでロールバック/ロールフォワード 処理をします。

不具合や抜け目があるかもしれませんが参考として。
    • good
    • 0

★時間差ですね。

驚き!
>ところで、用意する構造体は「ANo.7」と同じものでよろしいのでしょうか?
 ↑
 回答 No.13 の紹介ページを読んで見て下さい。
 ソートは比較関数を作成すれば簡単に機能追加できます。
 よってソート用に構造体を用意する必要はありません。
>それと、for文の中の処理はそれぞれ何行くらいになりますでしょうか?
 ↑
 20行~30行程度だと思うよ。
 だから別にサブ関数にしなくても良いが、分かりやすく関数にした方が良いです。
 また、サブ関数を作ればいろいろとお応用も利きますから。
 下にちょっとしたサンプルを載せておきます。

サンプル:
// リストビューに列データを挿入/追加
VOID subListViewItem( HWND hWnd, long pos, LPCTSTR lpString )
{
 static HWND hCtrl;
 static int subPos;
 LV_ITEM item;
 
 item.mask = LVIF_TEXT;
 item.iItem = pos;
 item.iSubItem = subPos;
 item.pszText = (LPTSTR)lpString;
 
 if ( hWnd != NULL ){
  item.iSubItem = subPos = 0;
  hCtrl = hWnd;
  ListView_InsertItem( hCtrl, &item );
 }
 else{
  ListView_SetItem( hCtrl, &item );
 }
 subPos++;
}

// 使い方
BOOL setListViewData( HWND hWnd, INT nPos, INT nPattern, LPCTSTR lpString )
{
 TCHAR szPattern[ 16 ];
 
 wsprintf( szPattern, TEXT("%d"), nPattern );
 subListViewItem( hWnd, nPos, szPattern ); // 最初の列
 subListViewItem( NULL, nPos, lpString ); // 続きの列
 return TRUE;
}

その他:
・上記のサンプルを参考に getListViewData() の関数も作ってみて下さい。
 そして for() 文の中に setListViewData() や getListViewData() を記述すれば
 良いだけです。
 readFile() 関数には setListViewData() 関数を for() 文の中で記述。
 writeFile() 関数には getListViewData() 関数を for() 文の中で記述。
 という事です。
・以上。あとは自分でお作り!

この回答への補足

多少変数名など変更させていただきましたが、教えていただいた通りにやりましたら、うまくいきました。
本当にどうもありがとうございました。
以下が、ソースの一部です。
//**********************************************************************
//
// リストビューからファイルに書き出す関数
//
//**********************************************************************
BOOL WriteFile(HWND hList, const char *lpFname)
{
 char szPattern[2];
 char szBuff[256];
 FILE *fp;
 int max;
 int no;
 int nPattern;
 
 if((fp = fopen(lpFname, "w")) != NULL){
  max = ListView_GetItemCount(hList);

  for(no = 0; no < max; no++){
   ListView_GetItemText(hList, no, 0, szPattern, sizeof(szPattern));
   ListView_GetItemText(hList, no, 1, szBuff, sizeof(szBuff));

   if(!strcmp(szPattern, "1")){
    nPattern = 1;
   }else if(!strcmp(szPattern, "2")){
    nPattern = 2;
   }else if(!strcmp(szPattern, "3")){
    nPattern = 3;
   }
   fprintf(fp, "%d,%s\n", nPattern, szBuff);
  }
  fclose(fp);
  return TRUE;
 }
 return FALSE;
}
//**********************************************************************
//
// リストビューに列データを挿入/追加
//
//**********************************************************************
void subListViewItem(HWND hWnd, int pos, const char *szBuff)
{
 static HWND hCtrl;
 static int subPos;
 LV_ITEM item;

 item.mask = LVIF_TEXT;
 item.iItem = pos;
 item.iSubItem = subPos;
 item.pszText = (LPTSTR)szBuff;

 if(hWnd != NULL){
  item.iSubItem = subPos = 0;
  hCtrl = hWnd;
  ListView_InsertItem(hCtrl, &item);
 }else{
  ListView_SetItem(hCtrl, &item);
 }
 subPos++;
}

// 上記関数を使う関数
BOOL setListViewData(HWND hWnd, INT nPos, int nPattern, const char *szBuff)
{
 char szPattern[16];

 wsprintf(szPattern, "%d", nPattern);
 subListViewItem(hWnd, nPos, szPattern); // 一列目
 subListViewItem(NULL, nPos, szBuff); // 二列目
 
 return TRUE;
}

補足日時:2007/07/27 19:47
    • good
    • 0
この回答へのお礼

<続き>
ただ、今回は特に構造体を使うことなくできてしまいました。
せっかく構造体を教えていただいたのに使わなくてすみません。
とにかく、まずはある程度形にしたいと思いまして・・・
動いたときは本当に嬉しかったです。
もちろん、この後構造体を作成し、ソートに挑戦したいと思います。
教えていただいたサイトを参考に、なんとか完成させたいと思います。

ところで、構造体を使わないことによるデメリットは、ソートを行えない以外に何かありますでしょうか。
ご教授の程、よろしくお願いいたします。

お礼日時:2007/07/27 20:00

★追記。


・ソートは ListView_SortItems() マクロ関数で出来ました。
 次のページにソートの方法が載っています。
 比較関数さえ作れば簡単に出来ます。
 http://www.kumei.ne.jp/c_lang/sdk2/sdk_110.htm→『第110章 リストビューのソート』
・以上。参考にどうぞ。

参考URL:http://www.kumei.ne.jp/c_lang/sdk2/sdk_110.htm
    • good
    • 0

★データ数が100個ですか。


・それなら全データの書き出しで良いでしょうね。
 どうせデータ入力を人間が行い『追加』、『更新』、『削除』ボタンを押すだけの
 使い方なのでしょ。ボタンを離したら瞬時に書き出しも終了していますよ。
・また、読み込みはプログラムの起動時に1回だけ行えば良いです。
 あとはリストビューにデータがありますので操作のたびにファイルに書き出すだけで
 データを一致させられます。
・よって作成する関数は2つです。
 1つ目はファイルからリストビューに読み込む関数。
 2つ目はリストビューからファイルに書き出す関数。
 プロトタイプを載せると
 (1)BOOL readFile( HWND hDlg, INT nID, LPCTSTR lpFname[] );
 (2)BOOL writeFile( HWND hDlg, INT nID, LPCTSTR lpFname[] );
 とまります。引数は
 HWND hDlg…ダイアログのウインドウハンドル
 INT nID……リストビューのID
 LPCTSTR lpFname[]…ファイル名
 で戻り値は正常なら TRUE、エラーなら FALSE を返す仕様です。
 こんな感じどう。
・ファイル形式は CSV にしましょう。
 そうすれば Excel でやり取りが出来たり、データの確認用としても使えます。
 CSV 形式は各データをカンマ文字(,)で区切っただけのテキストです。
 今回は『パターン番号』、『文字列』の2つをカンマ文字で区切って行単位で読み書きします。
 下にそのサンプルを載せておきます。

サンプル:
// ファイルからリストビューに読み込む
BOOL readFile( HWND hDlg, INT nID, LPCTSTR lpFname[] )
{
 char buff[ 256 ];
 FILE *fp;
 int type;
 int no;
 
 if ( (hDlg = GetDlgItem(hDlg,nID)) != NULL ){
  if ( (fp = fopen(lpFname,"r")) != NULL ){
   for ( no = 0 ; fscanf(fp,"%d,%s\n",&type,buff) == 2 ; no++ ){
    /*
    ★ここでリストビューに追加する処理★
    no:リストビューの行位置を表す(0-99)
    type:パターン番号(1-3)
    buff:固定文字列
    hDlg:リストビューのウインドウハンドル
    ※type のデータ値が 1~3 かチェックする処理を入れても良いかも。
    */
   }
   fclose( fp );
   return TRUE;
  }
 }
 return FALSE;
}

// リストビューからファイルに書き出す
BOOL writeFile( HWND hDlg, INT nID, LPCTSTR lpFname[] )
{
 char buff[ 256 ];
 FILE *fp;
 int type;
 int max;
 int no;
 
 if ( (hDlg = GetDlgItem(hDlg,nID)) != NULL ){
  if ( (fp = fopen(lpFname,"w")) != NULL ){
   max = ListView_GetItemCount( hDlg );
   
   for ( no = 0 ; no < max ; no++ ){
    /*
    ★ここでリストビューから取得する処理★
    no:リストビューの行位置を表す(0-99)
    type:パターン番号(1-3)
    buff:固定文字列
    hDlg:リストビューのウインドウハンドル
    ※type のデータ値が 1~3 かチェックする処理を入れても良いかも。
    */
    fprintf( fp, "%d,%s\n", type, buff );
   }
   fclose( fp );
   return TRUE;
  }
 }
 return FALSE;
}

その他:
・上記のコメント部分にリストビューとのやり取りを記述します。
 readFile() 関数では ListView_InsertItem()、ListView_SetItem() のマクロ関数を
 使って追加していきます。
 writeFile() 関数では ListView_GetItem() のマクロ関数を使って取得します。
・リストビューの設定と取得は別のサブ関数として作っておくと見やすくなります。
 関数のプロトタイプは次のような感じで。
 (1)BOOL setListViewData( HWND hWnd, int no, int pattern, LPCTSTR lpString );
 (2)BOOL getListViewData( HWND hWnd, int no, int *pattern, LPTSTR lpString );
 ※引数の no=行位置、pattern=パターン番号、lpString=固定文字列となります。
 ※戻り値は正常に設定や取得できれば TRUE、エラーなら FALSE を返す仕様です。
・使い方は WM_INITDIALOG の場所で readFile() 関数を1回だけ使います。
 その後は『追加』、『更新』、『削除』ボタンが押されたときの処理に1行だけ
 writeFile() 関数で書き出す行を追加すればよいだけです。

ソート:
・ソートは 100 個の構造体配列を malloc() で確保してその配列にリストビューの
 内容をすべて取り込みます。あとは qsort() 関数で昇順、降順のソートを行ます。
 ソート結果をリストビューのアイテムをすべてクリアしてから再登録するように
 すれば楽に出来ると思います。なので malloc() で確保するときに ListView_GetItemCount()
 でデータ数を取得しておきます。構造体配列のソートは qsrot() 関数と比較関数を
 用意すれば出来上がりです。あとはヘッダの『列』をクリックすればソートできるように
 機能追加すれば良いです。
・以上。
    • good
    • 0
この回答へのお礼

何度もご丁寧にアドバイス頂き、本当にありがとうございます。

ところで、用意する構造体は「ANo.7」と同じものでよろしいのでしょうか?

それと、for文の中の処理はそれぞれ何行くらいになりますでしょうか?

毎度お手数お掛けしますが、よろしくお願いいたします。

お礼日時:2007/07/27 12:45

★もっと簡単に考えてみますか。


・単純に追加、挿入、削除の操作を行ったらリストビューより全データを
 順番にファイルに書き出した方が楽かもしれないね。そしてプログラムの
 起動時にだけファイルから読み込む処理を1回行う。
・この考えなら簡単に実装できると思います。
 データの数があまり多くないのならばこれが一番お勧めですね。
 メモリも無駄なく、少量なら効率的かな。
・上記の方法ならファイルの読み込み関数、ファイルの書き込み関数の2つを
 用意すればもう出来ますね。リストビューの追加、削除などは出来ている
 ようなので。

補足要求:
・kenkenpo さんはファイル構造をどのように考えているのでしょうか?
 上記の簡単は方法ならテキストにした方が良いかもしれませんね。
 ファイル構造的には1行が
 (1)パターン番号
 (2)固定文字列
 の2つがあれば良いので CSV 形式と同じカンマ区切りにするのはどうでしょうか。
 CSV なら Excel でも読み込めたりします。
・データの数が 1000 個ぐらいなら全データの読み書きでもいいと思います。
 とにかくファイル構造はテキスト形式、バイナリ形式、データ量はどれぐらい。
 追加、削除のときに全データをファイルに書き出して反映させるタイプなのか、
 追加、削除のときに1データをファイルに書き出して反映させるタイプなのか
 どちらを考えていますか?→実装が簡単な方は全データの書き出しですけど。
・以上。補足要求します。はっきりしないと回答が混乱しちゃいます。決めて。

この回答への補足

お世話になっております。
こんな時間にお時間を取らせてしまってすみません。書き込みありがとうございました。
正直な所、あんなに関数が必要になるとは思っていませんでしたので、
プロトタイプ宣言を見て圧倒されております。

補足をさせていただきます。
・リストビューの指定行への挿入は考えておりません。追加と変更と削除のみです。
(ただ、できれば列をクリックすることで、パターンごとのソートができたらとは思っています。
少し調べてみましたところ、MFCを使わないと何だか難しそうな感じですが・・・)

・ファイル形式にこだわりはありません。と言いますか、どちらにどの様な
メリット・デメリットがあるのかもよく分かっていません。なるべく簡単な方法を希望します。
CSV形式で問題ありません。
(ただ、VTClientさんへの最初のお礼でも書かせていただいたのですが、
リストビューの内容を登録したファイルは、別のアプリからも使いたいと思っています。
具体的には、パターンごとに用意された3つの関数にそれぞれの文字列を引数として
渡すというものです。その処理を実装しやすいほうがいいです。)

・データ数は、たぶん多くても100個程度かと思われます。

・あまりプログラムのことが分からないなりに、毎回全データを読み書きしていたら、
処理速度が落ちるのかなと漠然と思ってはいたのですが、それがあまり気にならない程度でしたら
、実装が簡単な「全データをファイルに書き出して反映させるタイプ」の方が良いかもしれません。

何度もお手数をお掛けしてしまい申し訳ありませんが、よろしくお願い致します。

補足日時:2007/07/27 00:08
    • good
    • 0

No.9 です。


以下にリストを載せました。
一応これでエラーはなくなるはずです。
変数名をかえて、質問者さん用の処理を(ボタン無効など)登録、変更、削除の処理に
追加してください。
尚、対応変数は、No.4 を参照して下さい。

>例えば、fopenしてfread、fwriteするようなことはできますでしょうか。

for (i=0;i<=N;i++)
fwrite(…"%d %d %d %d %d %d",FList[i].index,FList[i].cur,FList[i].type,FList[i].back.index,FList[i].next.index,FList[i].size);
配列位置、対応位置、パターン、1つ前の配列位置、1つ先の配列位置、2桁目の文字データサイズ
(1桁目文字列は、パターンの値で判断)

という要領です。

同様に読み込みをしたら、

FileList *p;
int cur;

SetFocus(list);
ListView_DeleteAllItems(list);
MessageBox(hDlg,"確認します","列挙",0);
for (cur=0;FList[cur].cur;cur++) if (cur>=N) return 1;
for (p=&FList[cur];p->cur!=-1;p=p->next)
{
LVITEM cell;
char txBuf[20];

cell.mask=LVIF_TEXT;
cell.iItem=p->cur;
cell.iSubItem=0;
cell.pszText=txBuf;
switch (p->type)
{
case 1:
strcpy(txBuf,"パターンA");
break;
case 2:
strcpy(txBuf,"パターンB");
break;
case 3:
strcpy(txBuf,"パターンC");
}
ListView_InsertItem(list,&cell);
cell.iSubItem=1;
cell.pszText=p->txBuf;
ListView_SetItem(list,&cell);
}
でリストビューに表示反映できます。

>VTClient さんの4つの回答(アドバイス)をよく読むとリストビューと同じデータを…
Oh-Orange さん、ご指摘ありがとうございます!
間違いなくおっしゃるとおりですね。
普通に考えれば、Oh-Orange さんがご提示されましたNo5の内容が
私自身も最妥当と思います。
しかしよく考えてみれば、質問者さんの内容ですと、
「構造体を考慮したものとして…」
と示唆しています。
リストビューよりファイル書き出し前に最新状態を拾うのであれば
単に、リストビューアイテム構造体を先頭からシーケンシャルに周して書き出し
(読み込みはその逆)
すれば良いだけで、そもそもユーザー定義の構造体の必要がありませんよね?
その上で構造体視点で今回、考えたものです。

ただもし目的の反映さえできればあとはどーでも…と質問者さんお考えでしたら
(私が)でしゃばったものとしてすみません!
    • good
    • 0
この回答へのお礼

>しかしよく考えてみれば、質問者さんの内容ですと、
>「構造体を考慮したものとして…」
>と示唆しています。
>リストビューよりファイル書き出し前に最新状態を拾うのであれば
>単に、リストビューアイテム構造体を先頭からシーケンシャルに周し>て書き出し
>(読み込みはその逆)
>すれば良いだけで、そもそもユーザー定義の構造体の必要がありませ>んよね?
>その上で構造体視点で今回、考えたものです。
>ただもし目的の反映さえできればあとはどーでも…と質問者さんお考>えでしたら
>(私が)でしゃばったものとしてすみません!

VTClientさんがでしゃばったなんてとんでもございません。
ただ自分が、勝手に構造体を使えばいいんじゃないかなと思い込んでいただけでして、
現に構造体を使わずに目的の機能を実現できました。
VTClientさんがおっしゃるとおり、目的の反映さいできれば方法はどんなものでも良かったのです。
大変ご迷惑お掛けしました。失礼いたしました。
しかしながら、自分は構造体やポインタの理解がまだまだ足りていないので、
VTClientさんが提示してくださったソースコードは大変参考になります。
まだ完全には理解できておりませんが、隅々まで理解できるように頑張ります。
この度は、本当にありがとうございました。

お礼日時:2007/07/27 20:15

No.1,2,3,4 です。



冒頭で追加

#define N 100 // 最大項目数

struct FileList{
int index;// 配列位置
int cur;// 対応位置
char* txBuf;
int size;
int type;// パターン
FileList *back;
FileList *next;
} FList[N+1];

case WM_INITDIALOG:
for (int i=0;i<=N;i++)
{
FList[i].index=i;
FList[i].size=0;
FList[i].cur=-1;
FList[i].back=i?&FList[i-1]:&FList[i];
FList[i].next=i<N?&FList[i+1]:&FList[i];
}・・・

case IDC_BUTTON1://登録
{
LVITEM cell;
FileList *p,*p2,*p3;
int cur,cur2;
char txBuf[15];

sum=ListView_GetItemCount(list);
//index=ListView_GetSelectionMark(list)+1;// これで任意位置追加
index=sum;//これで常時最後位置追加
for (cur2=0;FList[cur2].cur!=-1;cur2++) if (cur2>N-2) return 1;
for (cur=cur2+1;FList[cur].cur!=-1;cur++) if (cur>N-1) return 1;
cell.mask=LVIF_TEXT;
cell.iItem=index;
cell.iSubItem=0;
cell.pszText=txBuf;
switch (state)
{
case 1:
strcpy(txBuf,"パターンA");
break;
case 2:
strcpy(txBuf,"パターンB");
break;
case 3:
strcpy(txBuf,"パターンC");
}
ListView_InsertItem(list,&cell);
cell.iSubItem=1;
GetDlgItemText(hDlg,IDC_EDIT1,txBuf,sizeof(txBuf));// txBuf=登録文字列
p3=&FList[cur];
p2=&FList[cur2];
if (p2->cur==-1) p2->next=p3;
if (sum) for (cur=0;FList[cur].cur!=(index?index-1:0);cur++);else cur=0;// cur=最後の行
p=&FList[cur];
p2->type=state;
p2->txBuf=(char*)malloc(p2->size=strlen(txBuf)+1);
strcpy(p2->txBuf,txBuf);
if (index)
{
p->next->back=p2;
p->next->next=p2->next;
p2->back=p;
p2->next=p->next->cur==-1?p3:p->next;
p->next=p2;
p2->cur=index;
for (p=p2->next;p->cur!=-1;p=p->next) p->cur++;
}
else
{
FileList *p4;
p->back=p2;
p2->next=sum?p:p3;
p2->back=p2;
for (p4=p;p4->next->cur!=-1;p4=p4->next);
p4->next=p3,p3->next=p3,p3->back=p4;
for (;p->cur!=-1;p->cur++,p=p->next);
p2->cur=index;
}
ListView_SetItem(list,&cell);
SetFocus(list);
return 1;
}
case IDC_BUTTON2://変更
{
LVITEM cell;
FileList *p;
int cur;
char txBuf[15];

cell.mask=LVIF_TEXT;
index=ListView_GetSelectionMark(list);
if (index<0) return 1;
cell.iItem=index;
cell.iSubItem=0;
cell.pszText=txBuf;
switch (state)
{
case 1:
strcpy(txBuf,"パターンA");
break;
case 2:
strcpy(txBuf,"パターンB");
break;
case 3:
strcpy(txBuf,"パターンC");
}
ListView_SetItem(list,&cell);
cell.iSubItem=1;
GetDlgItemText(hDlg,IDC_EDIT1,txBuf,sizeof(txBuf));
for (cur=0;FList[cur].cur!=index;cur++);
p=&FList[cur];
p->type=state;
if (p->size) free(p->txBuf);
p->txBuf=(char*)malloc(p->size=strlen(txBuf)+1);
strcpy(p->txBuf,txBuf);
ListView_SetItem(list,&cell);
SetFocus(list);
return 1;
}
case IDC_BUTTON3://削除
{
FileList *pf,*p;
int cur,sum;

index=ListView_GetSelectionMark(list);
if (index<0) return 1;
sum=ListView_GetItemCount(list);
ListView_DeleteItem(list,index);
for (cur=0;FList[cur].cur==-1;cur++);
for (;FList[cur].cur!=index;cur++) if (cur>N-1) return 1;
if ((pf=&FList[cur])!=pf->next) for (p=pf->next;p->cur!=-1;p=p->next) p->cur--;
pf->next->back=pf->back;
pf->back->next=pf->next;
if (pf->size) free(pf->txBuf);
pf->size=0,pf->cur=-1;
SetFocus(list);
return 1;
}
    • good
    • 0

続き。



★エラーコード関数
dataGetError  :エラーコードの取得
dataSetError  :エラーコードの設定

★データ管理関数
dataOpen   :ファイル構造の管理データを開く
dataClose  :ファイル構造の管理データを閉じる
dataAttach  :リストビューのウインドウハンドルをアタッチする

★基本操作関数
dataAppend  :ファイル構造とリストビューに追加
dataInsert  :ファイル構造とリストビューに挿入
dataChange  :ファイル構造とリストビューに更新(上書き)
dataDelete  :ファイル構造とリストビューに削除

★サブ関数セット1
subIDCheck   :ID文字列のチェック
subAlloc    :ファイル名,データ並び,データ有無を確保
subReadOpen  :ファイルからヘッダ構造をオープン
subCreateOpen :新規ファイルからヘッダ構造をオープン

★サブ関数セット2
subListViewItem  :リストビューに列データを挿入/追加
subInsertData   :リストビューに1つの構造体データを挿入
subDataExpand   :ファイル構造のデータブロックを拡張
subEmptySearch  :ファイル構造から空きデータを検索
subRead      :ファイル構造からデータを読み込む
subWrite     :ファイル構造にデータを書き込む

解説
・最低でも上記の関数群を用意する必要があります。
 この関数群はファイル構造のバイナリをオープンして管理ヘッダを作成してから
 プログラムが終了するまでファイルをオープンした状態で使います。
 そしてこの関数群で1つの構造体データをファイル構造とリストビューの同時に
 追加、挿入、削除、更新(上書き)する操作になります。
・上記の関数群をしっかりと用意さえすれば GUI のボタンから追加、挿入、削除
 などの操作が楽になります。またバグを発生させないですみます。おまけにメモリも
 リストビューにデータの実体を管理させるので節約できます。
・重要なのはリストビューに1構造体のデータを管理させて、そのデータの順番と
 ファイル構造の書き込み位置だけを管理するシステム作りが必要なのです。
 C++ 言語のクラスとして設計するともっと汎用性が良くなります。

その他:
・1つ1つ作成していけば、最後の GUI 操作が楽になりますよ。
 とにかくリストビューとファイル構造を関連付けて管理する必要があります。
 今回、紹介した構造体と関数群はディスク・ファイルの管理法である FAT などを
 参考にしてみました。FAT=『File Allocation Table』です。
・紹介した構造体(head_t,data_t)と回答 No.6 で図解したファイル構造と関数群の
 プロトタイプ宣言からリストビューとファイル構造の操作システムを構築するための
 整理と理解をして下さい。多分、初めて実装するので時間がかかると思いますが
 1個ずつ時間をかけ作っていけばいいと思います。
 私もちょっとだけ作成してみよう。試しに。
・最後にデータ量はどれぐらいですか?
 それにより簡略化できるかもしれない。
 紹介したものは大量のデータでも扱えるように考えています。

大まかなフローチャート:
(1)ファイルをオープンして管理ヘッダを作成
(2)管理ヘッダのデータ並び情報を読み込む
(3)管理ヘッダのデータ有無情報を読み込む
(4)GUI ボタンなどで追加、挿入、削除、更新の操作を行う
(5)管理ヘッダをファイルに保存してクローズ

以上。
    • good
    • 0

★ファイルはバイナリです。


 と回答 No.6 で書いているのですけど。
・idString はファイル形式を識別するために用意したものです。
 例えば BMP ファイルは先頭に "BM" という識別文字が必ずあります。
 プログラムではこの識別文字やヘッダ情報から本当に BMP ファイル形式かどうかを
 判別します。これと同じような考えでリストビュー構造のファイル形式に識別文字列の
 idString を用意したのです。だから無くても良いがあった方が便利かもという事だ。
>「データの構造体」の文字列は2つではなく1つです。
 パターン番号、固定文字列の2列と解釈すべきでしたか。
 失礼しました。国語苦手!
>主要な部分のソースコードだけでも提示していただけると幸いです。
 主要なソースコードだけでは多分理解できないでしょう、すべてのソースを
 サンプルで載せることが出来ません。
 かなり関数が多くなるので。
 その代わりに関数のプロトタイプ宣言を載せます。
 あと新しい構造体も。修正版。

// ヘッダ管理の構造体を宣言
typedef struct head_t {     // サイズ 60 バイト
 char idString[ 16 ];    // ファイル形式のID文字列
 long headSize;      // ヘッダのサイズ
 long allSize;      // 全データのサイズ
 long oneSize;      // 1データのサイズ
 long nowData;      // 登録データの個数
 long maxData;      // 最大データの個数
 HWND hWnd;       // リストビューのハンドル
 FILE *fp;       // ファイル・ポインタ
 char *fname;      // データのファイル名
 long *order;      // データの並び順配列
 char *exist;      // データの有無用配列
 char *alloc;      // 可変データ領域全体
} head_t;

// データ管理の構造体を宣言
typedef struct data_t {     // サイズ 260 バイト
 int pattern;      // パターン番号
 char str[ 256 ];     // 固定長文字列
} data_t;

// エラーコード関数
extern int dataGetError( void );
extern int dataSetError( int error );

// データ管理関数
extern head_t *dataOpen( const char dataFile[] );
extern head_t *dataClose( head_t *hp );
extern head_t *dataAttach( head_t *hp, HWND hWnd );

// 基本操作関数
extern int dataAppend( head_t *hp, data_t *dp );
extern int dataInsert( head_t *hp, long pos, data_t *dp );
extern int dataChange( head_t *hp, long pos, data_t *dp );
extern int dataDelete( head_t *hp, long pos );
extern int dataDefrag( head_t *hp );

// サブ関数セット1
static int subIDCheck( const char string[] );
static int subAlloc( head_t *hp, const char dataFile[] );
static head_t *subReadOpen( head_t *hp, const char dataFile[] );
static head_t *subCreateOpen( head_t *hp, const char dataFile[] );

// サブ関数セット2
static void subListViewItem( head_t *hp, long pos, const char string[] );
static void subInsertData( head_t *hp, long pos, data_t *dp );
static int subExpand( head_t *hp );
static long subEmptySearch( head_t *hp );
static void subRead( head_t *hp, long pos, data_t *dp );
static void subWrite( head_t *hp, long pos, data_t *dp );

まだ続く。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!