テキスト一行から複数項目の抽出

[戻る]
たこ吉 2005/08/19(金) 14:32:59
<前提条件>
・先頭行#はコメント行。
・データ行無いのデータは空白orタブ文字で区切られている。
・途中で空白行(改行のみの行)が入る場合もある。
・データ行の途中で区切られることはない。

下記データがバッファに格納済。
−−−−−−−−−−−ここから−−−−−−−−−−−
# 個人情報 YAMADA
#                 身長,   体重,   血液型,  足のサイズ, 性別
基礎情報           175      65      O        26.5      M
身体能力
     握力(左)        40     [kg]
     握力(右)        38     [kg]
     垂直跳び        70     [cm]
     100m走         70     [sec]

# 個人情報 YOSHIKAWA
#                 身長,   体重,   血液型,  足のサイズ, 性別
基礎情報           170      62      A        27.5      M
     握力(左)        40     [kg]
     握力(右)        38     [kg]
     垂直跳び        70     [cm]

# 個人情報 TAKEDA

                ・
                ・
                ・
−−−−−−−−−−−ここまで−−−−−−−−−−−
一人分のデータを、別々のエディットボックスに表示させて
画面上で修正したものを同一ファイルに上書き保存したいです。

しかしアルゴリズム(?)として良い方法が浮かびません。
単純にデータを空白で区切って切り出して表示するだけなら、
力技でなんとかなりそうですが、修正して上書きとなると・・・。
切り出し始めた位置等も考慮しないとならないですよね?

「私ならこうする!」と言う案をお聞きしたいのです。
宜しくお願いします。環境はVC6でMFC使ってます。
私はSTL使った事ないのですが、使うと楽に出来るのであれば
挑戦してみたいと思ってます。

長文になりましたが宜しくおねがいします。

επιστημη [E-Mail] 2005/08/19(金) 14:41:39
>「私ならこうする!」と言う案をお聞きしたいのです。

コード内で扱いやすいようにファイルの書式自体を変更する。
'ヒトが読むためのテキストファイル'なら↑から生成すればいい。

シャノン 2005/08/19(金) 15:06:37
> 画面上で修正したものを同一ファイルに上書き保存したいです。

変更箇所だけ上書きするのは面倒くさいので、変更前のファイルは削除してしまい、変
更のファイルを新たに同じ名前で作成し、データを全部吐き出す。

たこ吉 2005/08/19(金) 15:14:33
さっそくの回答ありがとうございます。

>コード内で扱いやすいようにファイルの書式自体を変更する。

これが出来ればいいんですけども・・・
直接エディタで書き換える場合もあるんです。(涙)
改行やらスペースなどを決め打ちで書いてしまうと
データが取れなくなる場合が怖いです。
またソフトウェアとして、ユーザが修正していない部分を
勝手に修正してしまうのは宜しくないと思ってます。
仮にユーザがエディタで「___123__」(_はスペース)としたものを
ツール側で変更していないのに、勝手に「___123」としてしまうのは
宜しくないって意味です。


やっぱり相当労力かかる事やろうとしてますね私。
最初からバイナリで作ってくれれば...ブツブツブツ...すみません。
もう少し皆さんのご意見お聞かせください。

たこ吉 2005/08/19(金) 15:33:40
>変更前のファイルは削除してしまい、変更のファイルを新たに同じ名前で作成し、
>データを全部吐き出す。

そのつもりです。
結局は「一行内のデータの抽出」と「変更後のデータセット」が一番の
問題なんですよね・・。

リンク 2005/08/19(金) 15:43:27
>・データ行の途中で区切られることはない。
>直接エディタで書き換える場合もあるんです

直接エディタで書き換えたとしてミスで途中で区切られたらどうするんでしょう?
>データが取れなくなる場合が怖いです。
ということから今の仕様ではどっちみちエディタでの書き換えがミスったら
データは取れなくなるのでは・・・

結局、直接書き換える人にも決め打ちを強制しないとうまく取れないのでは?

・・・つまり、決め打ちは駄目なのかということです。

επιστημη [E-Mail] 2005/08/19(金) 15:45:55
> 仮にユーザがエディタで「___123__」(_はスペース)としたものを
> ツール側で変更していないのに、勝手に「___123」としてしまうのは
> 宜しくないって意味です。

ってことは、元ファイルにある空白/空行/コメントの類がどこにいくつ
あったかを完全に把握し、可能な限りそれを維持しようというのですね。
元が ___1___ であるとき、これをアプリケーションで 123 に変更すると、
_123___ にする? ___123_ ? それとも他のなにか?
そんなことまで考えるつもりなら、相当労力かかる事やろうとしてますねアナタ (^^

>「私ならこうする!」と言う案をお聞きしたいのです。

割り切る。勝てない喧嘩を売らない。

シャノン 2005/08/19(金) 18:09:22
> 相当労力かかる事やろうとしてますねアナタ (^^

そんなに面倒くさいですかね?
まぁ、到底美しいとは言えない力技なコードになる自信はありますが。

いや、面倒くさいと言えば面倒くさいか。
「面倒くさい」と「難しい」は別ですからね。

tib 2005/08/19(金) 18:44:54
> 仮にユーザがエディタで「___123__」(_はスペース)としたものを
> ツール側で変更していないのに、勝手に「___123」としてしまうのは
> 宜しくないって意味です。
ならば、ユーザーが変更を加えたかどうかをチェックして、
変更していない行は元のファイルからのデータをコピーすれば
いいんじゃないでしょうか。

・a.datを読み込み画面に表示する。
 −ファイルから読み込んだままの1行と
  そこから抽出したデータを別々に持っておく。

・ユーザーがデータ編集する。
 −どの項目に修正を加えたかを把握しておく。

・編集済みのデータを保存する。
 −未編集のデータなら「ファイルから読み込んだままの1行」を出力。
 −編集済のデータなら「画面から得た値を使って1行を整形」。



「ツール側で変更していないのに、勝手に...」がNGなだけで、
「ツール側で変更しているなら、勝手に整形してもOK」と解釈しましたが
あってますか? これもNGだというなら、値が99から100に修正された時のように
データの桁数が変わった時にどういう出力になってほしいのかしっかり
決めておく必要があります。

こたつミカン 2005/08/20(土) 01:40:18
MFC入りのコンソールプログラムで簡単に作成してみました。
こんな感じでどうでしょう?

const int NAMEBUFFER_MAX = 100;    /* 名前の最大長 */
const int BODYAVILITYLIST_NUM = 4; /* リストの件数 */

struct BodyAbility{
    char* Kind;        /* 種類 */
    char* Unit;        /* 単位 */
};

const BodyAbility BodyAbilityList[BODYAVILITYLIST_NUM]= {
    {"握力(左)", "kg"},
    {"握力(右)", "kg"},
    {"垂直跳び", "kg"},
    {"100m走", "sec"},
};

/**
 * 一人分の個人データを格納する構造体
*/
struct DATA{
    char Name[NAMEBUFFER_MAX];    /* 名前 */
    int Height;                    /* 身長 */
    int Weight;                    /* 体重 */
    char BloodGroup;            /* 血液型 */
    float FootSize;                /* 足のサイズ */
    char Personality;            /* 性格 */
    float BodyAbilityData[BODYAVILITYLIST_NUM];/* 各種身体能力データ */
};

/**
 * フォーマットを作成して、データをバッファから読み込みます。
 *
 * @param LPCTSTR ReadLine 1行分の文字列バッファ
 * @param int ListID 先のBodyAbilityListの要素番号
 * @param float* pValue 値を格納するfloat型のポインタ
 *
 * @return データ取得結果が返ります。
 * @retval true 成功
 * @retval false 失敗
 */
bool SUBFUNC(LPCTSTR ReadLine, int ListID, float* pValue)
{
    CString Format;

    /* " 握力(左) %f [kg]" といったようなフォーマット用のバッファを作成し
ます。 */
    Format.Format(" %s %%f [%s]", BodyAbilityList[ListID].Kind, 
BodyAbilityList[ListID].Unit);
    
    /* sscanfで値を取得して格納できた数が1件だった場合 */
    if(::sscanf(ReadLine, (LPCTSTR)Format, pValue) == 1){
        return true;
    }
    else{
        return false;
    }
}

void FUNC(void)
{

    CStdioFile File;
    CString ReadLine;

    DATA data;
    ::memset(&data, 0x00, sizeof(data));

    /* ファイルを開く */
    File.Open("test.txt", CFile::modeReadWrite);


    /* 1行目:名前をNameに取得する */
    File.ReadString(ReadLine);    /* ファイルから一行読み込む */
    ::sscanf(ReadLine, " # 個人情報 %s", data.Name); /* 行を評価 */

    /* 2行目:明示的に書いてるだけで実際はなにも格納しない */
    File.ReadString(ReadLine);
    ::sscanf(ReadLine, " # 身長, 体重, 血液型, 足のサイズ, 性別");

    /* 3行目:各値を格納 */
    File.ReadString(ReadLine);
    ::sscanf(ReadLine, " 基礎情報 %d %d %c %f %c", &data.Height, 
&data.Weight, &data.BloodGroup, &data.FootSize, &data.Personality);

    /* 4行目:明示的に書いてるだけで実際はなにも格納しない */
    File.ReadString(ReadLine);
    ::sscanf(ReadLine, "身体能力");

    File.ReadString(ReadLine);

    int ListID;
    float GetValue;
    for(ListID = 0; ListID < BODYAVILITYLIST_NUM ; ListID++){
        if( SUBFUNC(ReadLine, ListID, &GetValue) == true ){
            data.BodyAbilityData[ListID] = GetValue;
            File.ReadString(ReadLine);
        }
    }

    File.Close();

};

test.txtにデータバッファに格納したものを保存して
一行ずつ読み込ませてます。
sscanf関数は、文字列を意図通りに読み込むのに大変便利な関数です。
特に定型化したものを読ませるときに重宝するので、MSDNヘルプをじっくり
読んでみてください。

たこ吉 2005/08/22(月) 15:25:39
[[解決]]
週末アクセスできませんで、お返事送れた事を
お詫びいたします。

皆さんの色々なご意見拝見いたしました。
が、やはり相当手間がかかるので「作る側で決め打ちにさせろ」
と捻じ伏せました。

なんかVCの手法とは別の方向になってしまいましたが、
色々と参考になるご意見ありがとうございました。

επιστημη [E-Mail] 2005/08/22(月) 16:08:09
> が、やはり相当手間がかかるので「作る側で決め打ちにさせろ」
> と捻じ伏せました。

「決め打ち」を人間に強制するのはかなり酷ではありましょう。
「緩い決め打ち」がよさげです。 

  - データの間は1つ以上の空白/タブで区切るべし。
  - ただし、アプリケーションを通すと、勝手に空白/タブ数を調整するから悪しからず。

みたいな。

古いスレッドには追加発言できません。