C言語関係掲示板

過去ログ

No.525.古い戦歴を削除しながらファイルに記録する

[戻る] [ホームページ]
No.4118

ファイルの機能について
投稿者---ショウ(2002/12/30 23:25:17)


始めまして、私は今C言語でオセロを作っています。
オセロは完成したのですが、この戦歴をファイルでテキストに保存し
過去の対戦の様子を見ることが出来るようにしようと思っています。
対戦の様子を書き込む方法はわかるのですが
何度も対戦を重ねると、量が永遠と増えていくのでそれは避けたいのです。

そこで、テキスト文書が一定の値を超えると、追加書き込みから
新規書き込みに変更、又は一番古い戦歴を消し、一定の量を保つ
つまり、書き込んだテキスト文書の行数やバイト数(文字数)などを調べ
その値がある値を越えると今までのものを消してしまいたいわけです。
どのようにしたらいいか、アドバイスいただけませんか?よろしくお願いします


(例) 戦歴は20行で、5戦分記録しておきたい。100行に到達したら
先頭の20行を消し、追加書き込みを行う。


No.4119

Re:ファイルの機能について
投稿者---かずま(2002/12/31 00:28:01)


#include <stdio.h>
#include <time.h>

#define FNAME     "history.txt"
#define RECLINES  5
#define MAXLINES  100
#define LINESIZE  128

int update(char **record)
{
    static char buf[MAXLINES][LINESIZE];
    int i, n;
    FILE *fp = fopen(FNAME, "r+");

    if (fp == NULL) {
        fp = fopen(FNAME, "w+");
        if (fp == NULL) return 0;
    }
    for (n = 0; n < MAXLINES && fgets(buf[n], LINESIZE, fp); n++)
        ;
    rewind(fp);
    for (i = (n == MAXLINES) ? RECLINES : 0; i < n; i++)
        fputs(buf[i], fp);
    for (i = 0; i < RECLINES; i++)
        fputs(record[i], fp);
    fclose(fp);
    return 1;
}

int main(void)
{
    char *record[] = {
        "Tue Dec 31 00:30:14 2002\n",
        "abcdefghijklmno\n",
        "pqrstuvwxyzABCD\n",
        "EFGHIJKLMNOPQRS\n",
        "TUVWXYZ12345678\n",
    };
    time_t t = time(0);

    record[0] = ctime(&t);
    if (!update(record))
        return printf("can't open history file\n"), 1;
    return 0;
}


No.4120

Re:ファイルの機能について
投稿者---かずま(2002/12/31 01:30:53)


> 例) 戦歴は20行で、5戦分記録しておきたい。100行に到達したら
> 先頭の20行を消し、追加書き込みを行う。

すみません。1ゲームの戦歴が 5行で、20戦分記録しておくものと勘違いしていました。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define FNAME     "history.txt"
#define RECLINES  20
#define MAXLINES  100

int update(char **record)
{
    char *line[MAXLINES], buf[1024];
    int i, n;
    FILE *fp = fopen(FNAME, "r+");

    if (fp == NULL&& (fp = fopen(FNAME, "w")) == NULL) return 0;
    for (n = 0; n < MAXLINES && fgets(buf, sizeof buf, fp); n++)
        if ((line[n] = strdup(buf)) == NULL) puts("out of memory"), exit(1);
    rewind(fp);
    for (i = (n==MAXLINES) ? RECLINES : 0; i < n; i++)
        fputs(line[i], fp);
    for (i = 0; i < RECLINES; i++)
        fputs(record[i], fp);
    fclose(fp);
    for (i = 0; i < n; i++)
        free(line[i]);
    return 1;
}

int main(void)
{
    char *record[] = {
        "Tue Dec 31 00:20:14 2002\n", "Akira\n", "Hiroshi\n", "--------\n",
        "abcd\n", "efgh\n", "ijkl\n", "mnop\n", "qrst\n", "uvwx\n", "yzAB\n",
        "CDEF\n", "GHIJ\n", "KLMN\n", "OPQR\n", "STUV\n", "WXYZ\n", "1234\n",
        "5678\n", "---------\n",
    };
    time_t t = time(0);

    record[0] = ctime(&t);
    if (!update(record))
        return printf("can't open history file\n"), 1;
    return 0;
}

行の長さにかかわらず、固定長の 2次元配列を用意するのは気に入らないので、
動的に確保するようにしました。

それにしても、どうして、1ゲームの戦歴に 20行も必要なんでしょうか。
オセロは、60手で勝負がつくので、盤面の位置を a〜z、A〜Z、1〜8 の 60文字で表せば、
1行で戦歴を表せると思うのですが。

No.4127

Re:ファイルの機能について
投稿者---ショウ(2003/01/01 23:08:05)


返事が遅くなって申し訳ありません、解答ありがとうございます。
>それにしても、どうして、1ゲームの戦歴に 20行も必要なんでしょうか。
いえ、これはあくまでも例でして、説明文だけでは説明不足かなと思い
例題としてこのような事をしたいのです、ということを理解していただきたく
あのような記述をしました、ぶっちゃけ行数は適当です。

折角解答をいただいたのですが、見たこと無いとこがいくつかあるようで・・・
解読に少し時間がかかりそうです。
これを参考に色々試してみます。また分からないとこがあれば来ますね。
それでは、ありがとうございました

No.4156

Re:ファイルの機能について
投稿者---ショウ(2003/01/04 23:29:28)


こちらのプログラムですが、どうしても分からない部分があるので質問させてください。

for (n = 0; n < MAXLINES && fgets(buf[n], LINESIZE, fp); n++)
;
rewind(fp);
この部分なのですが、bufは二次元配列で宣言されていますよね?
このようなパターンは始めてなので・・・どのようになっているのか予想できません。

for (i = (n == MAXLINES) ? RECLINES : 0; i < n; i++)
fputs(buf[i], fp);
こちらのfor文ですが、真ん中あたりにある?はどういう意味があるんですか?
これも色々調べまわったのですが、結局分かりませんでした。

この2つ以外は、調べて何とか意味は分かったのですが
やはり完全に理解していないというのでしょうか、ファイルに対して
苦手意識をもっているというのもありますが、一番分かっていないのは
どのようにして古い戦歴を削除されているのか、その辺の仕組みがよく分かりません。
本来ならば参考書を読み漁って解決すべきだと思いますが、それだけの時間が残されていません。どうか教えてください。よろしくお願いします。

No.4165

Re:ファイルの機能について
投稿者---かずま(2003/01/05 03:12:04)


> for (n = 0; n < MAXLINES && fgets(buf[n], LINESIZE, fp); n++)
> ;
> rewind(fp);
> この部分なのですが、bufは二次元配列で宣言されていますよね?
> このようなパターンは始めてなので・・・どのようになっているのか予想できません。

char a[128]; という宣言があった場合、
式の中で、a[3] と書くと、それは配列の要素であり、その型は char です。
a[3] は配列ではありません。
式の中で、a と書くと、それは配列ですが、これは、a[0] へのポインタの値に
変換されてしまいます。a の値は &a[0] ということです。

char buf[100][128]; という宣言があった場合、
式の中で、buf と書くと、それは配列です。
buf[n] と書くと、それは配列の要素です。その型は、char [128] です。
すなわち、要素がまた配列なのです。C の二次元配列とは、配列の配列なのです。
char buf[100][128]; という宣言は、1行 128文字の char の配列が、100行あるという
意味です。buf[n] は、n番目の 1行 (128文字の char の配列) です。
そして、buf[n] の値は &buf[n][0] ということですから、その行の先頭のアドレスです。


> for (i = (n == MAXLINES) ? RECLINES : 0; i < n; i++)
> fputs(buf[i], fp);
> こちらのfor文ですが、真ん中あたりにある?はどういう意味があるんですか?
> これも色々調べまわったのですが、結局分かりませんでした。

? : というのは三項演算子です。 a ? b : c という式は、a が 0 でなければ b、
a が 0 なら c という値を演算結果とします。
(n==100) ? 5 : 0 という式は、n が 100 なら、n==100 は 1 なので、式の値は 5、
n が 100 でなければ、n==100 は 0 なので、式の値は 0 となります。
i = (n==100) ? 5 : 0; というのは、if (n == 100) i = 5; else i = 0; とほぼ同じです。


> 苦手意識をもっているというのもありますが、一番分かっていないのは
> どのようにして古い戦歴を削除されているのか、その辺の仕組みがよく分かりません。
    if (n == 100)
        i = 5;
    else
        i = 0;
    for (; i < n; i++)
        fputs(buf[i], fp);
n が 100 のときは、5行目から、99行目までを出力することで、
先頭の 5行(0行目から 4行目まで)を削除しています。

No.4180

Re:ファイルの機能について
投稿者---かずま(2003/01/05 17:28:31)


> n が 100 のときは、5行目から、99行目までを出力することで、
> 先頭の 5行(0行目から 4行目まで)を削除しています。

n が 100 でないとき、今読み込んだ n 行すべてをもう一度、上書きして
いるのですが、これは無駄ですね。
    rewind(fp);
    for (i = (n == MAXLINES) ? RECLINES : 0; i < n; i++)
        fputs(buf[i], fp);
を
    if (n == MAXLINES) {
        rewind(fp);
        for (i = RECLINES; i < n; i++)
            fputs(buf[i], fp);
     }

に修正します。


No.4343

ありがとうございました
投稿者---ショウ(2003/01/13 20:00:28)


レスが遅くなって申し訳ありません。
教えていただいたものを参考に、自分で色々試してみた結果、
完全に完成させることが出来ました。
自分でも完全に理解したつもりです。本当にありがとうございました。
また自分の力でどうにもならなくなった時は、ここに来たいと思います。
それでは、本当にありがとうございました