- ベストアンサー
Win32アプリのデータ保存/読込の手段について
【環境】WinXP/VC++6.0/Win32アプリ(MFC未使用) 【件名】Win32アプリのデータ保存/読込の手段について お世話になります。 下記前提のWin32アプリの場合、外部ファイルへのデータ保存/読込を行う手段として、INIファイル操作のAPI GetPrivateProfileStruct()とWritePrivateProfileStruct()等 を使用することが妥当かどうかについて、ご意見・ご指摘いただけますでしょうか。 【前提】 1)扱うデータは全てバイナリで、各データは構造体単位で管理する。 2)各データ(構造体)の容量は20バイト~100バイト程度。 3)各データ(構造体)は、カテゴリーとキーの組み合わせで一意に識別する。 4)関連のあるデータ(構造体)は同じカテゴリーとして管理する。 5)Windows32bitのマシン上での動作のみを想定している。 6)ファイル内での各データ(構造体)の開始位置は可変としたい。 ※データ(構造体)の種類と、容量が増減(構造体メンバの増減)する可能性があるため。 7)可能な限り、レジストリを使用したくない。 8)データ保存/読込の頻度は、アプリの起動時・終了時のみ。 【私見】 下記Win32APIの仕様が、前提1)2)3)4)6)を容易に実現するのに適している。 GetPrivateProfileStruct() WritePrivateProfileStruct() 【懸念】 前提5)に関して、MSDNではこれらの関数は16ビットWindowsアプリケーションとの互換性を保つために用意されているもので、 Win32アプリケーションではレジストリを使用するよう推奨されています。 以上よろしくお願いいたします。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
とりあえず、質問者様が言われるように、一旦メモリなどに退避して新たに全体を書き出すって感じになると思うのですが。 ちなみに、普通に構造体を丸ごと扱っていると、メンバが増減した時にバージョン毎に構造体を作るなどして対応すると思うのですが、これだとその構造体を使うほうで面倒なので、私はよくファイルヘッダにバージョン情報を設けて、メンバ一つ一つを読み出す(書き出す)ようにしています。
その他の回答 (4)
- clsdi99
- ベストアンサー率63% (31/49)
>そこでもう一つ疑問なのですが、データ数が多いバイナリファイルを読み書きする場合、書込み位置と読込み位置は延々と #define するしかないのでしょうか??毎回全てのデータを読み書きするわけではないので、特定のデータだけをなるべく早くシークしたいのです。 普通はdefineなどで位置を決めないほうが良いのではないでしょうか? バイナリファイルにデータを入れる場合、例えば先頭にレコード数やサイズ情報などを入れておき、読み出すときに先頭の情報を読み込んで、抽出したいデータの位置を計算してそこにシークするとか。
お礼
ご回答ありがとうございます。 ご教授いただいたように、ファイル先頭をヘッダー領域として ・ヘッダーのサイズ ・各データの識別コード、バイト長、データ領域先頭からのオフセット などを書き込み、各データの入出力を行うときはヘッダー内を検索して、「ヘッダーサイズ+オフセット」の位置をシークする方法をとろうと思います。 今ひとつ悩んでいることがあります。 ある時点でバイナリ保存したあとで、データの種類が追加される場合、ヘッダー領域に追加が発生することによって、既存のデータ領域全体をその分だけ後ろへシフトさせる必要がでてくると思います。 このような場合、別途以下のようなプログラムを実行する以外に既存データを損なわない方法ありますでしょうか。 1)ファイルから全データをメモリまたは別ファイルへ書き出す。 2)変更後のヘッダーを新規ファイルに書き込む。 3)2)に続いて、1)で退避したデータと新たに追加となったデータを2)のファイルへ書き込む。 なにとぞよろしくお願いいたします。
- yosi_yosi
- ベストアンサー率35% (165/468)
Win32アプリでもGetPrivateProfile****、WritePrivateProfile****を使用しても特に問題ありません。(Vistaはどうなんだか・・・試してないので分かりません。) ただ問題としては、読み書きの速度は非常に遅いです。 数多くのキー、セクションがあるとそれは顕著です。 ご注意あれ。
お礼
>ただ問題としては、読み書きの速度は非常に遅いです。 >数多くのキー、セクションがあるとそれは顕著です。 30バイト程度の構造体を3000回、読み書きしてみましたが、30秒近くかかりました。内部で行われているであろうチェックサムの計算と、セクション・キーの検索がネックなのでしょうか。 GetPrivateProfileStruct() WritePrivateProfileStruct() は、自前でデータ位置をシークする必要がない反面、処理速度に問題があるとわかりました。 ですので、やはりバイナリファイルを使おうと思いました。 そこでもう一つ疑問なのですが、データ数が多いバイナリファイルを読み書きする場合、書込み位置と読込み位置は延々と #define するしかないのでしょうか??毎回全てのデータを読み書きするわけではないので、特定のデータだけをなるべく早くシークしたいのです。
- clsdi99
- ベストアンサー率63% (31/49)
いいと思いますよ。 WritePrivateProfileStructを使用するとバイナリデータは16進表記の文字列に変換されINIファイルに格納されます。 使い方も要は環境設定的に、INIファイルから取得して格納するだけですのでいいと思います。速度などを求める場合はもちろんバイナリファイルですが。 懸念に関しては、レジストリに書く事を推奨しているだけですのでいいんじゃないでしょうか?
お礼
clsdi99さん、ご回答ありがとうございます。 いろんな型のメンバを持つ構造体でWritePrivateProfileStruct,GetPrivateProfileStructを試してみました。確かに16進表記の文字列に変換されINIファイルに格納されるだけで、保存/読込みは可能とわかりました。 速度に関しては別途検討の上、バイナリファイルにするかどうか決めようと思います。
- GOGOV
- ベストアンサー率54% (12/22)
これらのAPIは以下の用に書かれているテキストファイルから データを読取り/書込みするものです。 ---------------- [SECTION] KEY=DATA ---------------- セクション名とキー名を指定してデータを取りますが、 ここをバイナリデータとはできないはずです。 バイナリファイルへ保存が良いのではないかと思います。
お礼
GOGOVさん、ご回答ありがとうございます。 実はまだ実際に試していないのですが、次のような動作を期待していました。 typedef struct { short int iData; DWORD dwData; .... }DATA; DATA outData,inData; outData.iData = .....;//値をセット outData.dwData= .....;//値をセット WritePrivateProfileStruct(&outData,(省略)) --------------------------------------- ↓テキストエディタで開いたイメージ↓ [SECTION] KEY=.......... ~~~~~~~~~~ ↑データ部分のみバイナリ表現 --------------------------------------- WritePrivateProfileStruct(&inData,(省略)) データ部分がバイナリ形式なら、WritePrivateProfileStruct()の引数に渡した構造体のメンバの型から、「最初のメンバは2バイトなので先頭から2バイト目までを iData へ格納。次のメンバは4バイトなので3~6バイト目までを dwData へ格納」というように判別してくれるのでは?と思っていました。 というのも、データ部分が文字列(テキスト形式)として格納されるのであれば、WritePrivateProfileStruct(&inData)は文字列の切れ目とinDataのメンバとの対応付けができないのでは?と思ったのです。(文字数とバイト数は必ずしも一致しないため) 前提 6) がなければ、迷わずバイナリファイルでの保存/読込を選択するのですが。 ともかく、実際に試してみようと思います。
お礼
ご回答ありがとうございます。 >バージョン毎に構造体を作るなどして対応すると思うのですが 思えばWin32APIはそのようにしているものが多いようですね。バージョンの違いをtypedefによって使用する側に意識させないなど。 >ファイルヘッダにバージョン情報を設けて ファイルヘッダのバージョン情報によって、バージョンに応じた構造体がどれなのかを判断し、該当するバージョンの構造体へ読み込む(書き出す)を行うということですね。 アドバイスを参考に、以下のようにしようと思います。 1)アプリのバージョンを #define しておく。 2)バージョンアップ時、データファイル全体を退避・全体を出力する。このときファイルヘッダに設けたバージョン情報に#defineの値をセットして書き出すことで、ファイルヘッダのバージョンと1)の#define 値とが常に同期がとれた状態を維持する。 3)データファイルの入出力時には、ファイルヘッダのバージョン情報を1)の#define値と比較して、最新バージョン・旧バージョンかに応じた構造体メンバへ入出力を行う。