向文件添加活动

adding activity to a file

提问人:Yehia Alhaddad 提问时间:11/14/2023 最后编辑:Yehia Alhaddad 更新时间:11/14/2023 访问量:71

问:

当我尝试将活动添加到我的文本文件中时,如下所示:

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

我无法添加它。当我将其添加到 S1 student 时,它不会添加行尾。有没有办法像这样在最后添加一个活动;E002 或 ;CM003中。只是我只想在行的末尾添加活动。

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;
}

除此之外,我试图更改代码在seek_cur中的位置。我不知道问题出在哪里。

C 函数 文件

评论

1赞 Jabberwocky 11/14/2023
您需要展示一个最小的可重现示例。还要编辑并显示我的文件,它看起来像这样正确格式。
0赞 William Pursell 11/14/2023
perror("StudentAccounts.txt")提供信息量更大的错误消息。不要让用户猜测文件的名称,也不要依赖用户先验地知道文件名是什么。
0赞 William Pursell 11/14/2023
您需要注意如何使用 .如果文件上存在读取错误,则可能会成为无限循环。feof
2赞 William Pursell 11/14/2023
您会发现,与其尝试插入数据,不如在处理结束时编写新文件并替换原始文件要容易得多。
1赞 Armali 11/14/2023
@Yehia Alhaddad – 您必须意识到,当您用较长的行覆盖一行时,您也会覆盖行后的内容(在常见的操作系统上)。

答:

1赞 Eric Postpischil 11/14/2023 #1

文件是字节序列。当您的文件“看起来像”时:

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

然后,文件中的字节(从第一行末尾附近开始)是 E、0、0、1、换行符、S、2、逗号、C、M 等。当你的程序寻找到第一行的末尾并用来写“;E002“,它不会在文件中插入这些字节;它覆盖文件中的字节。因此,新字节会覆盖换行符以及换行符后面的 S、2、C、M 和其他字节,从而破坏文件的第二行。fprintf

标准 C 库例程不提供任何在文件中插入字节的方法,大多数操作系统的 I/O 例程也不提供任何方法。基本上没有可行的方法可以做到这一点,因为文件的字节存储在磁盘上的连续位置(有些在磁盘块中物理上是连续的,有些在逻辑上是连续的,跨多个块),插入几个字节需要读取文件中的每个后续字节,并将所有这些字节重写到新位置。(插入整个磁盘块可能是可行的,尽管它会给文件系统带来复杂性。

要以您想要的方式更新文件,必须编写一个新文件,例如通过读取输入文件并将新版本写入不同的输出文件,然后您可以移动新的输出文件以替换原始输入文件,如果需要。

商业数据库通过使用各种数据结构来组织文件来避免这个问题,因此内容不是单个字符流,而是以块或其他单元为单位的记录。

评论

0赞 Yehia Alhaddad 11/14/2023
所以基本上我必须添加结构才能使代码工作,否则它不会?
0赞 Eric Postpischil 11/14/2023
@YehiaAlhaddad:您必须为文件内容设计结构,或者必须通过编写全新的文件来更新记录。
1赞 SmellyCat 11/14/2023 #2

至少有 4 个问题是显而易见的:

  • 在内存中更新记录后,需要更新整个文件。由于线的长度是可变的,因此不能只返回线的开头。
  • 为了覆盖整个文件,您需要将所有记录加载到内存中,即使您不会更新其中的大部分记录。
  • 如果加载时记录的活动计数超过 50,则需要将其重置回 50。否则,在输出记录时,数组将被溢出。
  • 当前用于读取文件的代码似乎无法识别换行符。