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

C语言向学生活动文件末尾追加活动代码失败问题求助

问题:C语言无法向学生活动文件指定行追加内容

我的学生活动文件格式如下:

S1,CM004;CM020;E001
S2,CM010;CM009;CM004;CM006;SM001;E001;SM002;PC007
S3,CM020;CM17;T001

我想给指定学生(比如S1)的活动行末尾追加活动代码(比如;E002或;CM003),但现有C语言代码实现失败,无法追加到行尾。我调整过fseek中SEEK_CUR的偏移量,问题仍未解决,相关代码如下:

typedef struct {
    char studentID[20];
    char activities[50][10];
    int activityCount;
} StudentDetails;


void addActivity(const char *studentID, const char *codeToAdd) {
    FILE *studentFile = fopen("StudentAccounts.txt", "r+");

    if (studentFile == NULL) {
        perror("Error opening student file");
        return;
    }

    StudentDetails studentDetails;
    int found = 0;

    // Read student's chosen activities
    while (fscanf(studentFile, "%19[^,],", studentDetails.studentID) == 1) {
        // Check if the student ID matches
        if (strcmp(studentDetails.studentID, studentID) == 0) {
            found = 1;
            studentDetails.activityCount = 0;

            // Read and store existing activities
            while (fscanf(studentFile, "%9[^;];", studentDetails.activities[studentDetails.activityCount]) == 1) {
                trimWhitespace(studentDetails.activities[studentDetails.activityCount]);
                studentDetails.activityCount++;

                if (studentDetails.activityCount >= 50) {
                    printf("Warning: Maximum number of activities reached.\n");
                    break;
                }
            }

            // Add the new activity if not already present
            int activityFound = 0;
            for (int i = 0; i < studentDetails.activityCount; i++) {
                if (strcmp(studentDetails.activities[i], codeToAdd) == 0) {
                    activityFound = 1;
                    break;
                }
            }

            if (!activityFound) {
                // Append the new activity code
                strncpy(studentDetails.activities[studentDetails.activityCount], codeToAdd, sizeof(studentDetails.activities[0]));
                studentDetails.activityCount++;

                // Move file pointer back to the beginning of the line
                fseek(studentFile, -strlen(studentDetails.studentID) - 1, SEEK_CUR);

                // Write updated activities to the file
                for (int i = 0; i < studentDetails.activityCount; i++) {
                    fprintf(studentFile, "%s;", studentDetails.activities[i]);
                }

                // Break out of the loop since we found and updated the student
                break;
            }
        }

        // Skip the rest of the line
        while (fgetc(studentFile) != '\n') {
            // Check for end of file
            if (feof(studentFile)) {
                break;
            }
        }
    }

    fclose(studentFile);

    if (!found) {
        printf("Student ID %s not found.\n", studentID);
    } else {
        printf("Activity code %s added to student %s.\n", codeToAdd, studentID);
    }
}


int studentRole(const char ID[20]) {
    int choice;
    char codeToChange[20];
    char codeToAdd[20];
    char codeToDelete[20];
    int coreCredits = 0;
    int coreModulesSelected = 0; // Count of selected core modules

    do {
        printf("\nMenu:\n");
        printf("1. View/Track activities and credits\n");
        printf("2. Select an Core Module\n");
        printf("3. Delete an Core Module\n");
        printf("4. Select Activity\n");
        printf("5. Delete an Activity\n");
        printf("6. Quit\n");
        printf("Enter your choice: ");
        if (scanf("%d", &choice) != 1) {
            printf("Invalid input. Please enter a number.\n");
            while (getchar() != '\n'); // Clear input buffer
            continue;
        }

        switch (choice) {
            case 1:
                ViewActivitiesWithPoints(ID); // Call the function to display credits and points
                break;
            case 2:
                ShowActivities("CM");
                printf("Enter the code of the activity to add: ");
                scanf("%s", codeToAdd);
                while (getchar() != '\n'); // Clear input buffer
                // Add the activity to the list of chosen activities and also write it to Activity.txt
                addActivity(ID,codeToAdd);
                break;
            case 3:
                printf("Enter the code of the activity to delete from your Activity.txt file: ");
                scanf("%s", codeToDelete);
                deleteActivity(ID, codeToDelete,"CM");
                break;
            case 4:
                ShowActivities("E");
                ShowActivities("SM");
                ShowActivities("W");
                ShowActivities("CS");
                ShowActivities("PS");
                ShowActivities("PC");
                ShowActivities("CMP");
                ShowActivities("T");

                break;
            case 5:
                printf("Enter the code of the core module to delete: ");
                scanf("%s", codeToDelete);
                //deleteData(activities, &count, "coreModules.txt", codeToDelete,"CM");
                break;
            case 6:
                printf("Goodbye!\n");
                break;
            default:
                printf("Invalid choice. Please try again.\n");
        }
    } while (choice != 6);
    return 0;
}

问题排查

  1. 文件指针定位完全错误:你用fseek(studentFile, -strlen(studentDetails.studentID) - 1, SEEK_CUR)试图回到目标行开头,但读取完活动后,文件指针已经到了行尾(或换行符),这个偏移量计算根本无法定位到正确位置。
  2. 活动读取逻辑遗漏最后一项:fscanf("%9[^;];")要求活动后必须跟分号,但文件中最后一个活动后面是换行符,导致最后一项活动无法被读取,写入后会丢失原有最后一项内容。
  3. 直接覆盖破坏文件结构:就算指针位置正确,直接写入新内容会覆盖后续行的开头(因为新内容长度比原行长),导致整个文件格式混乱。

修复方案

处理文本文件行修改最稳妥的方式是读取整个文件内容,修改目标行后重新写入整个文件,彻底规避直接定位写入的各种问题。修改后的addActivity函数如下:

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

typedef struct {
    char studentID[20];
    char activities[50][10];
    int activityCount;
} StudentDetails;

// 实现trimWhitespace函数(去除字符串首尾空白)
void trimWhitespace(char *str) {
    char *start = str;
    while (*start == ' ' || *start == '\t') start++;
    memmove(str, start, strlen(start)+1);
    char *end = str + strlen(str) - 1;
    while (end > str && (*end == ' ' || *end == '\t')) end--;
    *(end + 1) = '\0';
}

void addActivity(const char *studentID, const char *codeToAdd) {
    FILE *studentFile = fopen("StudentAccounts.txt", "r");
    if (studentFile == NULL) {
        perror("Error opening student file for reading");
        return;
    }

    // 创建临时文件存储修改后的内容
    FILE *tempFile = fopen("temp.txt", "w");
    if (tempFile == NULL) {
        perror("Error creating temp file");
        fclose(studentFile);
        return;
    }

    char line[256];
    int found = 0;

    while (fgets(line, sizeof(line), studentFile) != NULL) {
        // 去除行尾换行符
        line[strcspn(line, "\n")] = '\0';

        char currentID[20];
        if (sscanf(line, "%19[^,],", currentID) != 1) {
            // 格式错误,直接写入原行
            fprintf(tempFile, "%s\n", line);
            continue;
        }

        if (strcmp(currentID, studentID) == 0) {
            found = 1;
            StudentDetails studentDetails;
            strcpy(studentDetails.studentID, currentID);
            studentDetails.activityCount = 0;

            // 分割活动代码,正确读取所有项
            char *activityStr = strchr(line, ',') + 1;
            char *token = strtok(activityStr, ";");
            while (token != NULL && studentDetails.activityCount < 50) {
                trimWhitespace(token);
                strncpy(studentDetails.activities[studentDetails.activityCount], token, sizeof(studentDetails.activities[0])-1);
                studentDetails.activities[studentDetails.activityCount][sizeof(studentDetails.activities[0])-1] = '\0';
                studentDetails.activityCount++;
                token = strtok(NULL, ";");
            }

            // 检查活动是否已存在
            int activityFound = 0;
            for (int i = 0; i < studentDetails.activityCount; i++) {
                if (strcmp(studentDetails.activities[i], codeToAdd) == 0) {
                    activityFound = 1;
                    break;
                }
            }

            if (!activityFound) {
                if (studentDetails.activityCount >= 50) {
                    printf("Warning: Maximum number of activities reached.\n");
                } else {
                    strncpy(studentDetails.activities[studentDetails.activityCount], codeToAdd, sizeof(studentDetails.activities[0])-1);
                    studentDetails.activities[studentDetails.activityCount][sizeof(studentDetails.activities[0])-1] = '\0';
                    studentDetails.activityCount++;
                }
            }

            // 写入修改后的行
            fprintf(tempFile, "%s,", studentDetails.studentID);
            for (int i = 0; i < studentDetails.activityCount; i++) {
                if (i > 0) fprintf(tempFile, ";");
                fprintf(tempFile, "%s", studentDetails.activities[i]);
            }
            fprintf(tempFile, "\n");
        } else {
            // 写入未修改的原行
            fprintf(tempFile, "%s\n", line);
        }
    }

    fclose(studentFile);
    fclose(tempFile);

    // 替换原文件
    if (remove("StudentAccounts.txt") != 0) {
        perror("Error deleting original file");
        remove("temp.txt");
        return;
    }
    if (rename("temp.txt", "StudentAccounts.txt") != 0) {
        perror("Error renaming temp file");
        remove("temp.txt");
        return;
    }

    if (!found) {
        printf("Student ID %s not found.\n", studentID);
    } else {
        printf("Activity code %s added to student %s.\n", codeToAdd, studentID);
    }
}

// 原studentRole函数可保留不变,此处省略重复代码

修复说明

  1. 临时文件机制:读取原文件每一行,修改目标行后写入临时文件,最后替换原文件,彻底避免指针定位错误。
  2. 正确分割活动:用strtok分割活动字符串,能读取包括最后一项在内的所有活动,不会遗漏。
  3. 安全写入:严格按照ID,活动1;活动2;...的格式写入,确保文件结构不被破坏。
  4. 边界保护:增加字符串截断处理(防止缓冲区溢出),以及活动数量上限判断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 10:45:54