【问题标题】:C fprintf and fscanf saving game stateC fprintf 和 fscanf 保存游戏状态
【发布时间】:2020-01-07 20:52:22
【问题描述】:

我在使用 fscanf 和 fprintf 时遇到问题,我已将游戏状态保存到文件中,然后尝试从同一个文件中重新加载(下次打开程序时)

我有功能:

bool savegame(saved save) {
    FILE* fptr;
    fptr = fopen("savegame.txt", "w+");
    if (fptr == NULL)
        return 0;
    fprintf(fptr, "beeseed= %d\n", save.beeseed);
    for (int i = 0; i < 5; i++)
    {
        fprintf(fptr, "car %d pos x= %d y=%d\n", i, save.cars[i].pos.x, save.cars[i].pos.y);
        fprintf(fptr, "car %d speed= %d\n", i, save.cars[i].rand);
        fprintf(fptr, "obstacle %d pos x= %d y=%d\n", i, save.obstacles[i].pos.x, save.obstacles[i].pos.y);
        fprintf(fptr, "obstacle %d speed= %d\n", i, save.obstacles[i].rand);
        fprintf(fptr, "frogend %d= %d\n", i, save.frogend[i]);
    }
    fprintf(fptr, "health= %d\n", save.health);
    fprintf(fptr, "worldTime= %f\n", save.worldTime);
    fprintf(fptr, "frog pos x= %d y=%d\n", save.frog.x, save.frog.y);
    fprintf(fptr, "lost frog pos x= %d y= %d rand= %d\n", save.lostfrog.pos.x, save.lostfrog.pos.y, save.lostfrog.rand);
    fprintf(fptr, "score bee= %d score end= %d\n", save.score.bee, save.score.end);
    fprintf(fptr, "score flag= %d score frog= %d\n", save.score.flag, save.score.frog);
    fprintf(fptr, "score pos= %d score total= %d\n", save.score.pos, save.score.total);
    fclose(fptr);
    return 1;
}

这个很好用,我的意思是 - 我的文件正确地填充了值。 现在我尝试扫描这个文件:

bool loadgame(saved* save) {
    FILE* fptr;
    fptr = fopen("savegame.txt", "r");
    if (fptr == NULL)
        return 0;
    fscanf(fptr, "beeseed= %d", &save->beeseed);
    for (int i = 0; i < 5; i++)
    {

    fscanf(fptr, "car %d pos x= %d y=%d", i, &save->cars[i].pos.x, &save->cars[i].pos.y);
    fscanf(fptr, "car %d speed= %d", i, &save->cars[i].rand);
    fscanf(fptr, "obstacle %d pos x= %d y=%d", i, &save->obstacles[i].pos.x, &save->obstacles[i].pos.y);
    fscanf(fptr, "obstacle %d speed= %d", i, &save->obstacles[i].rand);
    fscanf(fptr, "frogend %d= %d", i, &save->frogend[i]);
    }
    fscanf(fptr, "health= %d", &save->health);
    fscanf(fptr, "worldTime= %lf", &save->worldTime);
    fscanf(fptr, "frog pos x= %d y=%d", &save->frog.x, &save->frog.y);
    fscanf(fptr, "lost frog pos x= %d y= %d rand= %d", &save->lostfrog.pos.x, &save->lostfrog.pos.y, &save->lostfrog.rand);
    fscanf(fptr, "score bee= %d score end= %d", &save->score.bee, &save->score.end);
    fscanf(fptr, "score flag= %d score frog= %d", &save->score.flag, &save->score.frog);
    fscanf(fptr, "score pos= %d score total= %d", &save->score.pos, &save->score.total);
    fclose(fptr);
    return 1;
}

它只适用于文件的第一行(即 beeseed= 3),其他的根本不起作用

我知道我可能不太了解 fscanf,但我已经在 google 上检查了很多问题,但我不知道如何使它正确 - 对我来说最重要的是 - 易于理解和可读(我可能可以在一个 fscanf 中完成所有操作,其中包含整个 .txt 文件和变量为 %d,但它看起来很乱)

我也尝试在每个 fscanf 的末尾给出 '\n' 但它会带来库错误

还有我的savegame.txt 文件:

beeseed= 8
car 0 pos x= 952 y=427
car 0 speed= 9
obstacle 0 pos x= 1028 y=217
obstacle 0 speed= 10
frogend 0= 0
car 1 pos x= 647 y=392
car 1 speed= 8
obstacle 1 pos x= 1131 y=182
obstacle 1 speed= 5
frogend 1= 1
car 2 pos x= 604 y=357
car 2 speed= 5
obstacle 2 pos x= -71 y=147
obstacle 2 speed= 5
frogend 2= 0
car 3 pos x= 437 y=322
car 3 speed= 6
obstacle 3 pos x= 320 y=112
obstacle 3 speed= 7
frogend 3= 0
car 4 pos x= 142 y=287
car 4 speed= 5
obstacle 4 pos x= 7 y=77
obstacle 4 speed= 7
frogend 4= 0
health= 5
worldTime= 4.892000
frog pos x= 320 y=462
lost frog pos x= 376 y= 182 rand= 188
score bee= 200 score end= 476
score flag= 0 score frog= 0
score pos= 0 score total= 120

问题可能是我的结构包含布尔值save.frogend[i] 是布尔形式并且需要 %d(也是 1/0)?

typedef struct {
int x;
int y;
}pos;

typedef struct {
    short int pos;
    short int end;
    short int frog;
    short int bee;
    short int total;
    short int flag;
} score;

typedef struct {
    bool frogend[5];
    score score;
    pos frog;
    struct {
        pos pos;
        int rand;
    }lostfrog;
    struct {
        pos pos;
        int rand;
    }obstacles[5];
    struct {
        pos pos;
        int rand;
    }cars[5];
    short int beeseed;
    double worldTime;
    int health;
}saved;

【问题讨论】:

  • 我猜%d 也可能存在问题-您的结构成员之一可能是longboolcharunsigned int,而不是int。需要把saved的定义贴出来让别人查。
  • 你忘了pos。
  • 你有:fscanf(fptr, "car %d pos x= %d y=%d", i, &amp;save-&gt;cars[i].pos.x, &amp;save-&gt;cars[i].pos.y); — 你需要将&amp;i 传递给fscanf()(或者,更确切地说,是i 以外的某个整数变量),或者使用%*d 禁止赋值。
  • Lidbey,为什么代码不检查fscanf()的返回值?
  • "试图在每个 fscanf 结束时给出 '\n'" 不是一个坏主意。然而,启用警告的优秀编译器会抱怨fscanf(fptr, "car %d pos x= %d y=%d", i, &amp;save-&gt;cars[i].pos.x, &amp;save-&gt;cars[i].pos.y);。通过启用所有警告来节省时间并提高调试速度。

标签: c scanf


【解决方案1】:

您不会跳过 fscanf() 格式字符串中的换行符。所以第二个fscanf() 会失败,因为它希望在最后一个y=%d 之后立即找到单词car,但是它们之间有一个换行符。

在每个格式字符串的开头放置一个空格。这将使它跳过任何前导空格。

另外,参数中的所有i 都应该是&amp;i。但是您并不想重新分配i,因为它是由for 循环头控制的。因此,您应该跳过将这些字段分配给任何变量;你可以用%*d来做到这一点。

bool loadgame(saved* save) {
    FILE* fptr;
    fptr = fopen("savegame.txt", "r");
    if (fptr == NULL)
        return 0;
    fscanf(fptr, "beeseed= %d", &save->beeseed);
    for (int i = 0; i < 5; i++)
    {

        fscanf(fptr, " car %*d pos x= %d y=%d", &save->cars[i].pos.x, &save->cars[i].pos.y);
        fscanf(fptr, " car %*d speed= %d", &save->cars[i].rand);
        fscanf(fptr, " obstacle %*d pos x= %d y=%d", &save->obstacles[i].pos.x, &save->obstacles[i].pos.y);
        fscanf(fptr, " obstacle %*d speed= %d", &save->obstacles[i].rand);
        fscanf(fptr, " frogend %*d= %d", &save->frogend[i]);
    }
    fscanf(fptr, " health= %d", &save->health);
    fscanf(fptr, " worldTime= %lf", &save->worldTime);
    fscanf(fptr, " frog pos x= %d y=%d", &save->frog.x, &save->frog.y);
    fscanf(fptr, " lost frog pos x= %d y= %d rand= %d", &save->lostfrog.pos.x, &save->lostfrog.pos.y, &save->lostfrog.rand);
    fscanf(fptr, " score bee= %d score end= %d", &save->score.bee, &save->score.end);
    fscanf(fptr, " score flag= %d score frog= %d", &save->score.flag, &save->score.frog);
    fscanf(fptr, " score pos= %d score total= %d", &save->score.pos, &save->score.total);
    fclose(fptr);
    return 1;
}

另一种选择是使用fgets() 读取每一行,然后使用sscanf() 从中提取值。

【讨论】:

  • 我已经更新了答案。扫描时您没有使用i 的地址。
  • 考虑扫描到一个临时变量并检查值是否等于i。其他一些错误检测方法也是合适的(例如,最后一行之后的哨兵字符串比检查每个 fscanf 返回值要少)
  • @M.M 我实际上考虑重写它以单独扫描i,然后扫描到&amp;save-&gt;XXX[i].YYY。但它似乎比帮助 OP 更复杂,如果保证数据按顺序写入,则没有必要。
  • 谢谢,这个空间和i 的问题很好用,我想如果我通过i 没有&amp; 签名它不会尝试传递任何值跨度>
  • 你为什么会这样想? fscanf() 无法区分,它会尝试使用该值作为写入地址。
【解决方案2】:
  1. 处理fscanf返回值。处理错误。
  2. 您无法扫描到i。启用编译器警告并修复它们。
  3. 与其他答案一样,扫描换行符。
  4. short int 的格式说明符是 %hd

至于第2点,做fscanf(fptr, "%d", i)是无效的,i不是一个有效的变量地址。使用%*d,您可以扫描一个整数而不保存结果:

for (int i = 0; i < 5; i++)
{
    int e = fscanf(fptr, " car %*d pos x= %d y=%d", &save->cars[i].pos.x, &save->cars[i].pos.y); 
    if (e != 2) abort();
    ...
}

但我会尽可能实现附加错误处理:

for (int i = 0; i < 5; i++)
{
    int read_i = 0;
    int e = fscanf(fptr, " car %d pos x= %d y=%d", &read_i, &save->cars[i].pos.x, &save->cars[i].pos.y); 
    if (e != 3) abort(); // handle error
    if (i != read_i) abort(); // handle error
    ...
}

问题可能是我的结构包含布尔值 save.frogend[i] 是布尔形式

例如,如果save.frongend[i] 的类型是bool,则需要扫描到具有适当类型的临时变量以匹配scanf 说明符。例如,您可以仅使用 %dint。然后检查这些值是否在适当的范围内。例如:

int temp = 0;
int e = fscanf(fptr, "frogend %*d= %d", &temp);
if (e != 1) abort();
if (temp != 0 && temp != 1) abort();
save->frogend[i] = temp;

我想把它们放在一起:

bool loadgame(saved* save) {
    FILE* fptr;
    fptr = fopen("savegame.txt", "r");
    if (fptr == NULL)
        return 0;
    int e = 0;
    e = fscanf(fptr, "beeseed= %hd", &save->beeseed);
    if (e != 1) return 0;
    for (int i = 0; i < 5; i++) {
        int i2 = 0;
        e = fscanf(fptr, " car %d pos x= %d y=%d", &i2, &save->cars[i].pos.x, &save->cars[i].pos.y);
        if (e != 3) return 0;
        if (i2 != i) return 0;
        e = fscanf(fptr, " car %d speed= %d", &i2, &save->cars[i].rand);
        if (e != 2) return 0;
        if (i2 != i) return 0;
        e = fscanf(fptr, " obstacle %d pos x= %d y=%d", &i2, &save->obstacles[i].pos.x, &save->obstacles[i].pos.y);
        if (e != 3) return 0;
        if (i2 != i) return 0;
        e = fscanf(fptr, " obstacle %d speed= %d", &i2, &save->obstacles[i].rand);
        if (e != 2) return 0;
        if (i2 != i) return 0;
        int temp = 0;
        e = fscanf(fptr, "frogend %d= %d", &i2, &temp);
        if (e != 2) return 0;
        if (i2 != i) return 0;
        if (temp != 0 && temp != 1) return 0;
        save->frogend[i] = temp;
    }
    e = fscanf(fptr, " health= %d", &save->health);
    if (e != 1) return 0;
    e = fscanf(fptr, " worldTime= %lf", &save->worldTime);
    if (e != 1) return 0;
    e = fscanf(fptr, " frog pos x= %d y=%d", &save->frog.x, &save->frog.y);
    if (e != 2) return 0;
    e = fscanf(fptr, " lost frog pos x= %d y= %d rand= %d", &save->lostfrog.pos.x, &save->lostfrog.pos.y, &save->lostfrog.rand);
    if (e != 3) return 0;
    e = fscanf(fptr, " score bee= %hd score end= %hd", &save->score.bee, &save->score.end);
    if (e != 2) return 0;
    e = fscanf(fptr, " score flag= %hd score frog= %hd", &save->score.flag, &save->score.frog);
    if (e != 2) return 0;
    e = fscanf(fptr, " score pos= %hd score total= %hd", &save->score.pos, &save->score.total);
    if (e != 2) return 0;
    fclose(fptr);
    return 1;
}

【讨论】:

  • 谢谢,它现在工作得很好,但我仍然会收到关于未正确分配类型的警告,只要我检查了我可以写 %h 而不是 %d 来分配短 int 类型,我不知道 bool 类型的标志是什么??
  • if(numofresults!='predicted value') return 0; 是正确的吗?我的意思是,它不会让代码太乱吗?
  • 分配给short int的是%hdboo 没有标志,所以你需要使用其他类型。如果你真的想,你可以使用%hhdchar,但实际上,与%d 没有区别。 C 中的错误处理很冗长。它很冗长,这并没有错。我想我只是习惯于在我发表的每个声明之后写if。我相信你的意思是写if(numofresults!='predicted value') return 0;
  • 我认为它没有什么区别,因为它工作得很好,但是在分配这些值之后如何摆脱 50 个警告呢?
  • 一次一个警告!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-07
  • 2011-03-15
  • 2018-12-19
相关资源
最近更新 更多