提问人:Yehia Alhaddad 提问时间:11/14/2023 最后编辑:Yehia Alhaddad 更新时间:11/14/2023 访问量:71
向文件添加活动
adding activity to a file
问:
当我尝试将活动添加到我的文本文件中时,如下所示:
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中的位置。我不知道问题出在哪里。
答:
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。否则,在输出记录时,数组将被溢出。
- 当前用于读取文件的代码似乎无法识别换行符。
评论
perror("StudentAccounts.txt")
提供信息量更大的错误消息。不要让用户猜测文件的名称,也不要依赖用户先验地知道文件名是什么。feof