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

C程序文件写入异常:除最后一条外其余记录结束时间缺失

问题:办公时间记录写入文件时缺失结束时间(除最后一条)

我开发的C程序在插入办公时间时出现异常:除最后一条录入的记录外,其余所有记录的结束时间都未被写入文件。

预期文件内容:

1 Monday 2 p.m. 3 p.m.
2 Friday 4 p.m. 7 p.m.
3 Thursday 5 p.m. 9 p.m.

实际写入内容:

1 Monday 2 p.m.
2 Friday 4 p.m.
3 Thursday 5 p.m. 9 p.m.

代码片段

结构体与全局变量

#define MAX_ELEMENTS 100
#define MAXIMUM_LENGTH 50

typedef struct {
    char teacher;
    int id;
    char day[MAXIMUM_LENGTH];
    char startingTime[MAXIMUM_LENGTH];
    char endingTime[MAXIMUM_LENGTH];
} officehours;

officehours list[MAX_ELEMENTS]; 
int c = 0;
char filenamep[20];

存储记录到文件的函数

void store_office_hour(char teacher, officehours data[], int count, char* filename) {
    FILE* fp = fopen(filename, "w");
    if (fp == NULL) {
        printf("ERROR in %s: Unable to open file.\n", __func__);
        return;
    }

    for (int i = 0; i < count; i++) {
        fprintf(fp, "%d %s %s %s\n", data[i].id, data[i].day, data[i].startingTime, data[i].endingTime);
        fprintf(stderr, "DEBUG: %d %s %s %s \n", data[i].id, data[i].day, data[i].startingTime, data[i].endingTime);
    }

    fclose(fp);
}

插入新记录的函数

void insert_office_hour(char teacher) {
      
    filenamep[0] = teacher;
    filenamep[1] = '\0';
    strcat(filenamep, ".txt");

    FILE* fp = fopen(filenamep, "r");
    if (fp != NULL) { 
        c = 0;
        while (fscanf(fp, "%d %s %s %s", &list[c].id, list[c].day, list[c].startingTime, list[c].endingTime) == 4) {
            c++;
        }
        fclose(fp);
    }

    if (c >= MAX_ELEMENTS) {
        printf("ERROR in %s: Database is full.\n", __func__);
        return;
    }

    officehours input;
    getchar();  
    printf("\t\tEnter identification number: ");
    scanf("%d", &input.id);
    getchar(); 

    printf("\t\tEnter a day: ");
    read_line(input.day, MAXIMUM_LENGTH);
 
    printf("\t\tEnter starting hour: ");
    read_line(input.startingTime, MAXIMUM_LENGTH);

    printf("\t\tEnter ending hour: ");
    read_line(input.endingTime, MAXIMUM_LENGTH);

    if (find_office_hour(list, c, input.id) != -1) {
        printf("This office hour already exists.\n");
        return;
    }

    list[c++] = input;

    store_office_hour(teacher, list, c, filenamep);
}

读取一行输入的函数

int read_line(char str[], int n) {
    int ch, i = 0;

    while (isspace(ch = getchar()));

    while (ch != '\n') {
        if (i < n - 1) 
            str[i++] = ch;
        ch = getchar();
    }
    str[i] = '\0'; 
    return i;
}

问题根源

核心问题出在字符串的读写格式不匹配:

  1. 时间字段(如2 p.m.)包含空格,但写入时使用%s格式符,会将带空格的字符串直接输出,导致一条记录被拆分为多个空白分隔的字段(比如2 p.m.会被拆成2和p.m.两个独立字段)。
  2. 读取现有记录时,使用fscanf的%s格式符,该格式会在空白字符处停止解析,错误拆分带空格的时间字段。例如读取1 Monday 2 p.m. 3 p.m.时,%s会把2读入startingTime,p.m.读入endingTime,剩下的3 p.m.会被当作下一条记录的起始内容,导致原有记录的endingTime被错误覆盖,再次写入时这些记录就缺失了正确的结束时间。

解决方案

1. 修改文件写入格式,使用分隔符区分字段

修改store_office_hour函数的写入逻辑,用逗号分隔字段,避免空格导致的拆分:

void store_office_hour(char teacher, officehours data[], int count, char* filename) {
    FILE* fp = fopen(filename, "w");
    if (fp == NULL) {
        printf("ERROR in %s: Unable to open file.\n", __func__);
        return;
    }

    for (int i = 0; i < count; i++) {
        // 用逗号分隔字段,确保带空格的字符串被作为整体存储
        fprintf(fp, "%d,%s,%s,%s\n", data[i].id, data[i].day, data[i].startingTime, data[i].endingTime);
        fprintf(stderr, "DEBUG: %d,%s,%s,%s \n", data[i].id, data[i].day, data[i].startingTime, data[i].endingTime);
    }

    fclose(fp);
}

2. 修改文件读取逻辑,按分隔符解析整行内容

不再使用fscanf,改用fgets读取整行,再用strtok按逗号拆分字段:

void insert_office_hour(char teacher) {
      
    filenamep[0] = teacher;
    filenamep[1] = '\0';
    strcat(filenamep, ".txt");

    FILE* fp = fopen(filenamep, "r");
    if (fp != NULL) { 
        c = 0;
        char line[MAXIMUM_LENGTH * 4]; // 足够容纳一行的所有内容
        while (fgets(line, sizeof(line), fp) != NULL) {
            // 去掉行尾的换行符
            line[strcspn(line, "\n")] = '\0';
            
            // 按逗号拆分字段
            char* token = strtok(line, ",");
            if (!token) continue;
            list[c].id = atoi(token);

            token = strtok(NULL, ",");
            if (!token) continue;
            strncpy(list[c].day, token, MAXIMUM_LENGTH - 1);
            list[c].day[MAXIMUM_LENGTH - 1] = '\0';

            token = strtok(NULL, ",");
            if (!token) continue;
            strncpy(list[c].startingTime, token, MAXIMUM_LENGTH - 1);
            list[c].startingTime[MAXIMUM_LENGTH - 1] = '\0';

            token = strtok(NULL, ",");
            if (!token) continue;
            strncpy(list[c].endingTime, token, MAXIMUM_LENGTH - 1);
            list[c].endingTime[MAXIMUM_LENGTH - 1] = '\0';

            c++;
            if (c >= MAX_ELEMENTS) break;
        }
        fclose(fp);
    }

    // 后续插入逻辑保持不变
    if (c >= MAX_ELEMENTS) {
        printf("ERROR in %s: Database is full.\n", __func__);
        return;
    }

    officehours input;
    getchar();  
    printf("\t\tEnter identification number: ");
    scanf("%d", &input.id);
    getchar(); 

    printf("\t\tEnter a day: ");
    read_line(input.day, MAXIMUM_LENGTH);
 
    printf("\t\tEnter starting hour: ");
    read_line(input.startingTime, MAXIMUM_LENGTH);

    printf("\t\tEnter ending hour: ");
    read_line(input.endingTime, MAXIMUM_LENGTH);

    if (find_office_hour(list, c, input.id) != -1) {
        printf("This office hour already exists.\n");
        return;
    }

    list[c++] = input;

    store_office_hour(teacher, list, c, filenamep);
}

3. 修复read_line函数的边界问题

处理getchar()返回EOF的情况,避免未定义行为:

int read_line(char str[], int n) {
    int ch, i = 0;

    // 跳过开头空白,同时处理EOF
    while ((ch = getchar()) != EOF && isspace((unsigned char)ch));
    if (ch == EOF) {
        str[0] = '\0';
        return 0;
    }
   
    while (ch != '\n' && ch != EOF) {
        if (i < n - 1) 
            str[i++] = ch;
        ch = getchar();
    }
    str[i] = '\0'; 
    return i;
}

验证修改效果

修改后,文件内容会变成逗号分隔的格式:

1,Monday,2 p.m.,3 p.m.
2,Friday,4 p.m.,7 p.m.
3,Thursday,5 p.m.,9 p.m.

读取时会正确解析每个字段,再次写入时所有记录的结束时间都会被完整保存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 17:49:51