You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tic-tac-toe游戏存档加载异常:无法从TXT文件读取棋盘状态

井字棋游戏存档加载问题修复

我正在开发一款Tic-tac-toe(井字棋)游戏,支持根据用户输入打印棋盘,目前已实现棋盘状态、网格尺寸及回合数的保存功能,但从TXT文件加载数据时,棋盘状态无法成功读取。

原FileLoad函数代码

void FileLoad(char ***Board, int *x, int *y, int *roundcount, const char *filename) {
    FILE *Load =fopen(filename, "r");
    if (Load == NULL) {
        printf("Error");
        return;
    }

    fscanf(Load, "%d", *roundcount);
    fgetc(Load);
    fscanf(Load, "%d %d ", *x, *y);

    *Board = (char **)malloc(*y * sizeof(char *));

    (*Board)[0] = (char *)malloc((*y * *x) * sizeof(char));
    for (int i = 1; i < *y; ++i)
         (*Board)[i] = (*Board)[0] + i * *x;

    fgetc(Load);

    for (int i = 0; i < *y; ++i) {
        for (int j = 0; j < *x; ++j)
            fscanf(Load, "%c", &(*Board)[i][j]);
        fgetc(Load);
    }
    fclose(Load);
}

调试后确认问题出在最后读取棋盘状态的fscanf代码段,以下是5*5网格的存档文件示例:

2
5 5
X    
  O  

(注:存档中每行对应棋盘的一行,包含空格、X/O字符)

问题分析与修复方案

1. fscanf参数错误(核心问题)

原代码中读取整数时传递的是变量值而非地址:

  • fscanf(Load, "%d", *roundcount); 错误,*roundcount是int类型的值,fscanf需要的是变量的指针地址,应改为fscanf(Load, "%d", roundcount);
  • 同理,fscanf(Load, "%d %d ", *x, *y); 改为fscanf(Load, "%d %d", x, y);,同时去掉格式串末尾的空格,fscanf会自动跳过空白字符(换行、空格等)

2. 多余的fgetc调用导致字符丢失

原代码中手动调用fgetc跳过换行符,但fscanf读取整数后会自动跳过后续空白,手动调用会误读棋盘的第一个有效字符,需移除这些多余的fgetc,仅保留一次跳过尺寸读取后剩余换行的操作。

3. 棋盘读取逻辑错误

用fscanf("%c")会读取所有空白字符(包括换行),导致棋盘行列错位。改用fgets读取每行数据,确保每行对应棋盘的一行,再将字符复制到棋盘数组中。

修改后的完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void FileLoad(char ***Board, int *x, int *y, int *roundcount, const char *filename) {
    FILE *Load = fopen(filename, "r");
    if (Load == NULL) {
        printf("Error opening file");
        return;
    }

    // 读取回合数,检查读取是否成功
    if (fscanf(Load, "%d", roundcount) != 1) {
        printf("Error reading round count");
        fclose(Load);
        return;
    }

    // 读取网格尺寸x和y
    if (fscanf(Load, "%d %d", x, y) != 2) {
        printf("Error reading board dimensions");
        fclose(Load);
        return;
    }

    // 分配棋盘内存
    *Board = (char **)malloc(*y * sizeof(char *));
    if (*Board == NULL) {
        printf("Memory allocation error");
        fclose(Load);
        return;
    }

    (*Board)[0] = (char *)malloc((*y * *x) * sizeof(char));
    if ((*Board)[0] == NULL) {
        free(*Board);
        printf("Memory allocation error");
        fclose(Load);
        return;
    }

    for (int i = 1; i < *y; ++i) {
        (*Board)[i] = (*Board)[0] + i * *x;
    }

    // 跳过尺寸读取后剩余的换行符
    char dummy;
    while ((dummy = fgetc(Load)) != '\n' && dummy != EOF);

    // 逐行读取棋盘数据
    char line[100]; // 可根据最大网格尺寸调整数组大小
    for (int i = 0; i < *y; ++i) {
        if (fgets(line, sizeof(line), Load) == NULL) {
            printf("Error reading board line %d", i);
            // 释放已分配内存避免泄漏
            free((*Board)[0]);
            free(*Board);
            fclose(Load);
            return;
        }

        // 去掉行尾的换行符
        size_t len = strlen(line);
        if (len > 0 && line[len-1] == '\n') {
            line[len-1] = '\0';
        }

        // 将行内容复制到棋盘,不足部分用空格填充
        for (int j = 0; j < *x; ++j) {
            (*Board)[i][j] = (j < strlen(line)) ? line[j] : ' ';
        }
    }

    fclose(Load);
}

额外优化说明

  • 增加了错误检查逻辑,避免文件格式错误或内存分配失败导致程序崩溃
  • 处理了存档行长度不足的情况,用空格填充确保棋盘每个位置都有有效字符
  • 保留了原代码的连续内存分配方式,避免碎片化

内容的提问来源于stack exchange,提问作者Moppity

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 15:25:33