C语言中删除并重命名两个文本文件时触发段错误
葡萄园招聘助手系统段错误问题排查与修复
在实现「Drink My Friend」葡萄园招聘助手管理系统时,第二次调用addApplicant后,执行删除records.txt并将temp.txt重命名为records.txt操作时出现段错误。尝试在deleteThirdLineWithNumber函数调用前后手动关闭文件,问题仍未解决。
需求背景
春天已至「Drink My Friend」葡萄园,需招聘春季可工作的助手。求职者需提供姓名及可工作的星期几,农场各日期所需工人数量固定,满员则不再接收申请。需实现求职者数据的增删改查及列表生成功能。
问题代码
#include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> enum days_of_week { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }; void deleteThirdLineWithNumber(const char* filename) { FILE* fp = fopen(filename, "r+"); if (fp == NULL) { printf("Error opening file\n"); return; } char buffer[100]; int lineNumber = 1; int numberFound = 0; while (fgets(buffer, sizeof(buffer), fp) != NULL) { if (lineNumber == 3 && isdigit(buffer[0])) { numberFound = 1; break; } lineNumber++; } if (numberFound) { fseek(fp, 0, SEEK_SET); int currentLine = 1; FILE* tmp = fopen("temp2.txt", "w"); while (fgets(buffer, sizeof(buffer), fp) != NULL) { if (currentLine != 3) { fprintf(tmp, "%s", buffer); } currentLine++; } fclose(fp); fclose(tmp); remove(filename); rename("temp2.txt", filename); } else { printf("No number found on third line\n"); fclose(fp); } } void addApplicant(char *name, char *day, int week,char *filename) { int weekIndex = 0; if(strcmp(day, "MONDAY") == 0){ weekIndex = week*7 + MONDAY; } else if(strcmp(day, "TUESDAY") == 0){ weekIndex = week*7 + TUESDAY; } else if(strcmp(day, "WEDNESDAY") == 0){ weekIndex = week*7 +WEDNESDAY; } else if(strcmp(day, "THURSDAY") == 0){ weekIndex = week*7 + THURSDAY; } else if(strcmp(day, "FRIDAY") == 0){ weekIndex = week*7+FRIDAY; } else if(strcmp(day, "SATURDAY") == 0){ weekIndex = week*7+THURSDAY; } else if(strcmp(day, "SUNDAY") == 0){ weekIndex = SUNDAY + 7*week; } if (weekIndex < 0 || weekIndex >= 84) { printf("Sorry, %s, week %d does not exist.\n", day, weekIndex); return; } FILE *fp,*temp; fp = fopen(filename, "r"); temp = fopen("temp.txt", "w+"); if (fp == NULL && temp ==NULL) { printf("Error opening file\n"); return; } char buffer[100]; for (int i = 0; i < 84*3; i++) { if(i % 3 == 0) { fprintf(temp,"%s",fgets(buffer, sizeof(buffer), fp)); fgets(buffer, sizeof(buffer), fp); } else if ( i % 3 == 1) { int numberOfHelpers = atoi(buffer); char ch[100]; if (i == (weekIndex*3 +1)) { if (numberOfHelpers <= 0) { printf("Sorry, the maximum number of helpers has been reached for %s, week %d.\n", day, week); } fprintf(temp, "%d\n", numberOfHelpers-1); } fprintf(temp, "%d\n", numberOfHelpers); fgets(buffer, sizeof(buffer), fp); } else { if (i == (weekIndex*3 +2)) { char* names =(buffer); int pos = strcspn(names, "\n"); // find position of newline character if (pos < strlen(names)) { names[pos] = '\0'; // replace newline character with null character } strcat(names, name); printf("%s",names); fprintf(temp,"%s\n",names); } } } fclose(fp); fclose(temp); deleteThirdLineWithNumber("temp.txt"); remove(filename); rename("temp.txt",filename); } void initialisingFile(int maximumnumberOfhelper) { char day[10]; FILE *fp; fp = fopen("records.txt", "w"); if (fp == NULL) { printf("Error opening file\n"); return; } for (int i = 0; i < 84; i++) { if (i % 7 == 0) { strcpy(day, "MONDAY "); } else if (i % 7 == 1) { strcpy(day, "TUESDAY "); } else if (i % 7 == 2) { strcpy(day, "WEDNESDAY"); } else if (i % 7 == 3) { strcpy(day, "THURSDAY "); } else if (i % 7 == 4) { strcpy(day, "FRIDAY "); } else if (i % 7 == 5) { strcpy(day, "SATURDAY "); } else { strcpy(day, "SUNDAY "); } fprintf(fp, "%s\n", day); fprintf(fp, "%d\n", maximumnumberOfhelper); fprintf(fp, "%s\n", "people:"); } fclose(fp); } int main() { initialisingFile(10); addApplicant("sarveshwar", "MONDAY", 0,"records.txt"); addApplicant("sarveshwar", "MONDAY", 0,"records.txt"); return 0; }
问题分析
文件读取逻辑混乱
addApplicant循环中i%3==0分支连续调用两次fgets,但仅写入一次内容到临时文件,导致后续buffer内容与预期不符,甚至出现文件指针越界读取,触发非法内存访问。i%3==2分支仅在匹配目标索引时写入内容,其余情况完全跳过,导致临时文件缺失大量行,后续deleteThirdLineWithNumber处理时读取到不完整的文件,引发错误。
栈内存溢出
- 在
i%3==2的目标索引分支中,直接对栈上的buffer执行strcat操作,当原buffer内容加上name的长度超过buffer的100字节上限时,会导致栈溢出,直接触发段错误。
- 在
星期枚举值错误
- 处理
SATURDAY时,错误使用了THURSDAY的枚举值,导致对应日期的索引计算错误,后续操作会修改错误的行。
- 处理
文件操作无错误检查
deleteThirdLineWithNumber和addApplicant中的文件删除、重命名操作未做错误检查,若操作失败会导致后续流程异常。
修复后的代码
#include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> enum days_of_week { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }; void deleteThirdLineWithNumber(const char* filename) { FILE* fp = fopen(filename, "r"); if (fp == NULL) { printf("Error opening file\n"); return; } char buffer[100]; int lineNumber = 1; int numberFound = 0; while (fgets(buffer, sizeof(buffer), fp) != NULL) { if (lineNumber == 3 && isdigit(buffer[0])) { numberFound = 1; break; } lineNumber++; } if (numberFound) { fseek(fp, 0, SEEK_SET); int currentLine = 1; FILE* tmp = fopen("temp2.txt", "w"); if (tmp == NULL) { printf("Error creating temp file\n"); fclose(fp); return; } while (fgets(buffer, sizeof(buffer), fp) != NULL) { if (currentLine != 3) { fprintf(tmp, "%s", buffer); } currentLine++; } fclose(fp); fclose(tmp); if (remove(filename) != 0) { printf("Error removing original file\n"); remove("temp2.txt"); return; } if (rename("temp2.txt", filename) != 0) { printf("Error renaming temp file\n"); remove("temp2.txt"); return; } } else { printf("No number found on third line\n"); fclose(fp); } } void addApplicant(char *name, char *day, int week, char *filename) { int weekIndex = -1; if(strcmp(day, "MONDAY") == 0){ weekIndex = week*7 + MONDAY; } else if(strcmp(day, "TUESDAY") == 0){ weekIndex = week*7 + TUESDAY; } else if(strcmp(day, "WEDNESDAY") == 0){ weekIndex = week*7 + WEDNESDAY; } else if(strcmp(day, "THURSDAY") == 0){ weekIndex = week*7 + THURSDAY; } else if(strcmp(day, "FRIDAY") == 0){ weekIndex = week*7 + FRIDAY; } else if(strcmp(day, "SATURDAY") == 0){ weekIndex = week*7 + SATURDAY; } else if(strcmp(day, "SUNDAY") == 0){ weekIndex = week*7 + SUNDAY; } if (weekIndex < 0 || weekIndex >= 84) { printf("Sorry, %s week %d does not exist.\n", day, week); return; } FILE *fp = fopen(filename, "r"); FILE *temp = fopen("temp.txt", "w"); if (fp == NULL || temp == NULL) { printf("Error opening file\n"); if (fp) fclose(fp); if (temp) fclose(temp); return; } char buffer[100]; int currentEntry = 0; while (currentEntry < 84) { if (!fgets(buffer, sizeof(buffer), fp)) break; fprintf(temp, "%s", buffer); if (!fgets(buffer, sizeof(buffer), fp)) break; int numberOfHelpers = atoi(buffer); if (currentEntry == weekIndex) { if (numberOfHelpers <= 0) { printf("Sorry, the maximum number of helpers has been reached for %s week %d.\n", day, week); fprintf(temp, "%d\n", numberOfHelpers); } else { fprintf(temp, "%d\n", numberOfHelpers - 1); } } else { fprintf(temp, "%s", buffer); } if (!fgets(buffer, sizeof(buffer), fp)) break; if (currentEntry == weekIndex && numberOfHelpers > 0) { buffer[strcspn(buffer, "\n")] = '\0'; char names[200]; strcpy(names, buffer); strcat(names, " "); strcat(names, name); fprintf(temp, "%s\n", names); } else { fprintf(temp, "%s", buffer); } currentEntry++; } fclose(fp); fclose(temp); deleteThirdLineWithNumber("temp.txt"); if (remove(filename) != 0) { printf("Error removing original file\n"); remove("temp.txt"); return; } if (rename("temp.txt", filename) != 0) { printf("Error renaming temp file\n"); remove("temp.txt"); return; } } void initialisingFile(int maximumnumberOfhelper) { char day[10]; FILE *fp = fopen("records.txt", "w"); if (fp == NULL) { printf("Error opening file\n"); return; } for (int i = 0; i < 84; i++) { switch (i % 7) { case 0: strcpy(day, "MONDAY "); break; case 1: strcpy(day, "TUESDAY "); break; case 2: strcpy(day, "WEDNESDAY"); break; case 3: strcpy(day, "THURSDAY "); break; case 4: strcpy(day, "FRIDAY "); break; case 5: strcpy(day, "SATURDAY "); break; case 6: strcpy(day, "SUNDAY "); break; } fprintf(fp, "%s\n", day); fprintf(fp, "%d\n", maximumnumberOfhelper); fprintf(fp, "people:\n"); } fclose(fp); } int main() { initialisingFile(10); addApplicant("sarveshwar", "MONDAY", 0, "records.txt"); addApplicant("sarveshwar", "MONDAY", 0, "records.txt"); return 0; }
修复说明
- 重构
addApplicant的文件读取逻辑,按每个日期条目(3行一组)读取和写入,避免原循环中混乱的fgets调用和内容缺失问题。 - 将姓名拼接操作改为使用更大的临时数组,避免栈溢出风险。
- 修复
SATURDAY对应的枚举值错误。 - 在文件删除和重命名操作中增加错误检查,避免操作失败后引发异常。
- 调整
deleteThirdLineWithNumber的文件打开模式为r,并增加临时文件创建的错误检查。
内容的提问来源于stack exchange,提问作者Sarveshwar Singh
相关产品推荐
相关产品推荐

