C语言写入txt文件时吞掉输入字符串首字符、格式异常问题排查
问题描述
该函数用于向txt文件写入3项用户输入内容,分别为ID、分类(category)、描述(description),其中ID应为整数类型,其余两项为字符串类型。
问题复现代码
#include <stdio.h> #include <stdlib.h> void addtask(); char menu(); int main() { char selection ; do { selection = menu() ; switch(selection) { case '1': addtask(); break; case '2': break; default: printf("\n\n\nInvalid key entered"); } }while(selection != '2'); return 0; } char menu() { char option; printf("\nWelcome to the task management system. Please enter a key to continue...\n"); printf("\n1) Add new task"); printf("\n2) Save and quit\n"); scanf("%c[^\n]", &option); return option; } void addtask() { int id;char category[21];char description[101]; printf("\nCreating new task...\n\n"); FILE * f = fopen("tasklist.txt", "r+"); if(f == NULL){ printf("File does not exist. Creating new file..."); f = fopen("tasklist.txt", "w"); } else { fseek(f, 0, SEEK_END); //move to end of cursor printf("\nENTER id:"); scanf("%d", &id); printf("\nEnter category (max 20 characters): "); getchar(); fgets(category, 21, stdin); printf("\nEnter description (max 100 characters): "); getchar(); fgets(description, 101, stdin); printf("writing: %d \t %s \t %s \t", id, category, description); fprintf(f,"%d \t %s \t %s \t", id, category, description); } fclose(f); }
异常表现
运行程序并输入2组ID、分类、描述内容后,txt文件中实际输出内容如下:
1 Cat1 esc1 2 Cat2 esc2
预期正确输出应为:
1 Cat1 Desc1 2 Cat2 Desc2
问题原因分析
代码存在4个核心问题,直接导致输出异常:
- 首次创建文件时写入逻辑完全缺失:当
tasklist.txt不存在时,代码仅以写模式创建空文件,没有执行任何输入读取、写入操作就直接关闭文件,首次运行选择添加任务时根本不会写入任何内容。 - 输入缓冲区处理逻辑错误:
scanf("%d", &id)读取整数时,会把用户输入ID后按下的回车符\n留在输入缓冲区中。读取描述前额外多调用了一次getchar(),会直接吞掉用户输入的描述内容的第一个字符——这就是输入Desc1最终文件里显示esc1的直接原因,首字母D被多余的getchar()吃掉了。fgets()读取字符串时,会把用户输入完内容后按下的回车符一并存入字符串末尾,写入文件时就会自带换行,导致Cat1写完直接换行,和后面的描述内容错位。
menu()函数的scanf格式符写法错误:%c[^\n]是无效写法,%c仅匹配单个字符,后面跟[^\n]不符合格式规则,会导致菜单选项读取异常,残留的换行符还会干扰后续输入流程。- 文件打开逻辑冗余易错:原代码先用
r+模式打开文件,不存在再切w模式,把所有写入逻辑全放在else分支里,是导致首次创建文件无法写入的根本原因。
修复方案
对应问题逐一修改即可:
- 直接用追加模式
a打开文件,该模式下文件不存在会自动创建,存在则自动将光标定位到文件末尾,不需要手动调用fseek,也不用分支判断文件是否存在。 - 修正menu函数的scanf格式,用
scanf(" %c", &option),格式串前的空格会自动跳过所有残留的空白字符(换行、空格等),每次读取完选项后清空输入缓冲区。 - 移除读取描述前多余的
getchar(),每次用scanf读取整数后统一清空缓冲区到换行,避免残留字符干扰后续输入。 - 读取完category和description后,手动将字符串末尾fgets读入的换行符替换为字符串结束符
\0,避免写入文件时出现多余换行。
修复后的核心代码参考:
#include <stdio.h> #include <stdlib.h> #include <string.h> void addtask(); char menu(); int main() { char selection ; do { selection = menu() ; switch(selection) { case '1': addtask(); break; case '2': break; default: printf("\n\n\nInvalid key entered"); } }while(selection != '2'); return 0; } char menu() { char option; printf("\nWelcome to the task management system. Please enter a key to continue...\n"); printf("\n1) Add new task"); printf("\n2) Save and quit\n"); scanf(" %c", &option); // 清空缓冲区残留字符 while(getchar() != '\n'); return option; } void addtask() { int id;char category[21];char description[101]; printf("\nCreating new task...\n\n"); // 直接用追加模式打开,自动处理不存在/光标定位逻辑 FILE * f = fopen("tasklist.txt", "a"); if(f == NULL){ printf("Failed to open file!\n"); return; } printf("\nENTER id:"); scanf("%d", &id); // 清空scanf残留的换行 while(getchar() != '\n'); printf("\nEnter category (max 20 characters): "); fgets(category, 21, stdin); // 截断fgets读入的换行符 category[strcspn(category, "\n")] = '\0'; printf("\nEnter description (max 100 characters): "); fgets(description, 101, stdin); // 截断fgets读入的换行符 description[strcspn(description, "\n")] = '\0'; printf("writing: %d \t %s \t %s \n", id, category, description); fprintf(f,"%d \t %s \t %s \n", id, category, description); fclose(f); }
内容的提问来源于stack exchange,提问作者user19375357
相关产品推荐
相关产品推荐

