C 中的二进制文件
Binary Files in C
我一定要在大学里做一个C游戏
我在读写二进制文件时遇到了实际问题。
我已经问过监视器,阅读了书中关于二进制文件的一章,并尝试在 Google 中搜索任何可以帮助我更好地理解我遇到的问题的东西,但是 [=其中 43=] 个正常工作。
游戏中有一个结构,其中包含我们需要保存给玩家的所有信息,以便玩家在他喜欢时重新启动游戏。我们必须将所有不同的游戏保存在一个二进制文件中,并使用名称进行识别。所以,我做了一个读取二进制文件的算法,如果它存在,如果它找到一个与玩家同名的游戏,它会用新游戏覆盖它,如果它不存在,它会写在最后文件。
我们还必须读取文件以加载以前的游戏。
我做了一个小程序来测试只有玩家名字的结构的想法,但我遇到了一个问题:当我保存时,它似乎工作正常,但当我尝试加载它时, 它显示玩家名字的两倍甚至三倍(我把命令 put 和名字放在一起,以防程序找到一个用相同名字保存的游戏)。
另一件让我感到困惑的事情是,当我用不同的名称保存两个不同的游戏时,当我尝试加载第一个时,它只显示一次。
我真的很感激你能给我的任何帮助来解释为什么会这样。
我的代码:
int main()
{
GAME game;
GAME jogoTeste;
FILE *saved_game;
int i = 0;
int choice, saved = 0;
gets(game.player.name);
printf("Choose what you want to do:\n");
printf("1 to save, 2 to load\n");
scanf("%d", &choice);
if (choice == 1)
{
i = 0;
saved_game = fopen("saved_game.dat", "a+b");
rewind(saved_game);
while (!feof(saved_game))
{
fread(&testeGame, sizeof(GAME), 1, saved_game);
if (!strcmp(testGame.player.name, game.player.name))
{
int control = fwrite(&game, sizeof(GAME), 1, saved_game);
if (control != 1)
{
printf("An error happened in the wrintting\n");
printf("Were written %d elements intead of 1.\n", control);
}
else
{
saved = 1;
printf("Player updaded!\n");
}
}
}
if (saved == 0);
{
fwrite(&game, sizeof(GAME), 1, saved_game);
printf("New player saved\n");
}
fclose(saved_game);
}
else if (escolha == 2)
{
i = 0;
saved_game = fopen("saved_game.dat", "rb");
while (!feof(saved_game))
{
fread(&testGame, sizeof(GAME), 1, saved_game);
if (strcmp(testGame.player.name, game.player.name) == 0)
{
puts(testGame.player.name);
}
}
fclose(saved_game);
}
}
而结构体 GAME 是(其他的在这里并不重要):
typedef struct
{
PLAYER player;
GUARD guards[5];
KEY keys[5];
COORDINATE walls[50];
COORDINATE tower[50];
COORDINATE ogre;
} GAME;
如果有任何葡萄牙语是因为我现在没有看到它而忘记在这里翻译它。对不起[=13=]
编辑:
我尝试了你提到的一些改动,重复我的加载游戏的错误消失了,但现在有一些东西真的让我很困惑,我现在保存游戏的代码是:
saved = 0;
saved_game = fopen("saved_game.dat", "r+b");
while (fread(&jogoTeste, sizeof(GAME), 1, saved_game))
{
if (strcmp(jogoTeste.player.name, game.player.name) == 0)
{
fseek(saved_game, currentOffSet, SEEK_SET);
control = fwrite(&game, sizeof(GAME), 1, saved_game);
if (control != 1)
{
printf("An error happened in the wrintting\n");
printf("Were written %d elements intead of 1.\n", control);
}
else
{
saved = 1;
printf("Player updated!\n");
}
fseek(saved_game, 0, SEEK_END);
}
currentOffSet = ftell(saved_game);
}
if (!saved);
{
fwrite(&game, sizeof(GAME), 1, saved_game);
printf("New player saved\n");
}
fclose(saved_game);
但是当我执行它时,它在屏幕上给我:"Player updated!" AND "New player saved",怎么可能?当它更新播放器时,它还会将保存的变量更改为 1,并且在 "New player saved" 之前有一个 if 就是为了这个。它怎么会执行两个不应该执行的部分?
主要问题是 while(!feof(...)){}
构造。由于竞争条件,这会执行随机数量的追加。简单的解决方法是完全删除循环。代码的许多其他问题可能不会影响其操作,但应该作为良好实践进行修复(使用 getc
、多余的 rewind
等,请参阅注释)。
编辑:OP 在讨论后更改了代码,因此按照上面的建议删除循环是行不通的。正确的解决方法是将读取文件分开以查找播放器的名称,也许设置一个布尔标志,然后如果设置了布尔值则执行追加。
在你的代码中
fread(&testeGame, sizeof(GAME), 1, saved_game);
if (!strcmp(testGame.player.name, game.player.name))
{
int control = fwrite(&game, sizeof(GAME), 1, saved_game);
fread后,文件指针向前移动,然后fwrite到fread所在的位置。您需要将文件指针移回以覆盖旧记录,即使用 ftell 获取您所在的位置,然后使用 fseek 返回到该位置,或者使用 fseek 相对于当前文件位置向后移动 sizeof(GAME) 字节。
例如
currentOffset = ftell(saved_game);
fread(&testeGame, sizeof(GAME), 1, saved_game);
if (!strcmp(testGame.player.name, game.player.name))
{
fseek(saved_game, currentOffset, SEEK_SET);
int control = fwrite(&game, sizeof(GAME), 1, saved_game);
我一定要在大学里做一个C游戏
我在读写二进制文件时遇到了实际问题。
我已经问过监视器,阅读了书中关于二进制文件的一章,并尝试在 Google 中搜索任何可以帮助我更好地理解我遇到的问题的东西,但是 [=其中 43=] 个正常工作。
游戏中有一个结构,其中包含我们需要保存给玩家的所有信息,以便玩家在他喜欢时重新启动游戏。我们必须将所有不同的游戏保存在一个二进制文件中,并使用名称进行识别。所以,我做了一个读取二进制文件的算法,如果它存在,如果它找到一个与玩家同名的游戏,它会用新游戏覆盖它,如果它不存在,它会写在最后文件。
我们还必须读取文件以加载以前的游戏。
我做了一个小程序来测试只有玩家名字的结构的想法,但我遇到了一个问题:当我保存时,它似乎工作正常,但当我尝试加载它时, 它显示玩家名字的两倍甚至三倍(我把命令 put 和名字放在一起,以防程序找到一个用相同名字保存的游戏)。
另一件让我感到困惑的事情是,当我用不同的名称保存两个不同的游戏时,当我尝试加载第一个时,它只显示一次。
我真的很感激你能给我的任何帮助来解释为什么会这样。
我的代码:
int main()
{
GAME game;
GAME jogoTeste;
FILE *saved_game;
int i = 0;
int choice, saved = 0;
gets(game.player.name);
printf("Choose what you want to do:\n");
printf("1 to save, 2 to load\n");
scanf("%d", &choice);
if (choice == 1)
{
i = 0;
saved_game = fopen("saved_game.dat", "a+b");
rewind(saved_game);
while (!feof(saved_game))
{
fread(&testeGame, sizeof(GAME), 1, saved_game);
if (!strcmp(testGame.player.name, game.player.name))
{
int control = fwrite(&game, sizeof(GAME), 1, saved_game);
if (control != 1)
{
printf("An error happened in the wrintting\n");
printf("Were written %d elements intead of 1.\n", control);
}
else
{
saved = 1;
printf("Player updaded!\n");
}
}
}
if (saved == 0);
{
fwrite(&game, sizeof(GAME), 1, saved_game);
printf("New player saved\n");
}
fclose(saved_game);
}
else if (escolha == 2)
{
i = 0;
saved_game = fopen("saved_game.dat", "rb");
while (!feof(saved_game))
{
fread(&testGame, sizeof(GAME), 1, saved_game);
if (strcmp(testGame.player.name, game.player.name) == 0)
{
puts(testGame.player.name);
}
}
fclose(saved_game);
}
}
而结构体 GAME 是(其他的在这里并不重要):
typedef struct
{
PLAYER player;
GUARD guards[5];
KEY keys[5];
COORDINATE walls[50];
COORDINATE tower[50];
COORDINATE ogre;
} GAME;
如果有任何葡萄牙语是因为我现在没有看到它而忘记在这里翻译它。对不起[=13=]
编辑: 我尝试了你提到的一些改动,重复我的加载游戏的错误消失了,但现在有一些东西真的让我很困惑,我现在保存游戏的代码是:
saved = 0;
saved_game = fopen("saved_game.dat", "r+b");
while (fread(&jogoTeste, sizeof(GAME), 1, saved_game))
{
if (strcmp(jogoTeste.player.name, game.player.name) == 0)
{
fseek(saved_game, currentOffSet, SEEK_SET);
control = fwrite(&game, sizeof(GAME), 1, saved_game);
if (control != 1)
{
printf("An error happened in the wrintting\n");
printf("Were written %d elements intead of 1.\n", control);
}
else
{
saved = 1;
printf("Player updated!\n");
}
fseek(saved_game, 0, SEEK_END);
}
currentOffSet = ftell(saved_game);
}
if (!saved);
{
fwrite(&game, sizeof(GAME), 1, saved_game);
printf("New player saved\n");
}
fclose(saved_game);
但是当我执行它时,它在屏幕上给我:"Player updated!" AND "New player saved",怎么可能?当它更新播放器时,它还会将保存的变量更改为 1,并且在 "New player saved" 之前有一个 if 就是为了这个。它怎么会执行两个不应该执行的部分?
主要问题是 while(!feof(...)){}
构造。由于竞争条件,这会执行随机数量的追加。简单的解决方法是完全删除循环。代码的许多其他问题可能不会影响其操作,但应该作为良好实践进行修复(使用 getc
、多余的 rewind
等,请参阅注释)。
编辑:OP 在讨论后更改了代码,因此按照上面的建议删除循环是行不通的。正确的解决方法是将读取文件分开以查找播放器的名称,也许设置一个布尔标志,然后如果设置了布尔值则执行追加。
在你的代码中
fread(&testeGame, sizeof(GAME), 1, saved_game);
if (!strcmp(testGame.player.name, game.player.name))
{
int control = fwrite(&game, sizeof(GAME), 1, saved_game);
fread后,文件指针向前移动,然后fwrite到fread所在的位置。您需要将文件指针移回以覆盖旧记录,即使用 ftell 获取您所在的位置,然后使用 fseek 返回到该位置,或者使用 fseek 相对于当前文件位置向后移动 sizeof(GAME) 字节。
例如
currentOffset = ftell(saved_game);
fread(&testeGame, sizeof(GAME), 1, saved_game);
if (!strcmp(testGame.player.name, game.player.name))
{
fseek(saved_game, currentOffset, SEEK_SET);
int control = fwrite(&game, sizeof(GAME), 1, saved_game);