C语言中使用gets()写入文件时出现多余空字符的问题
问题分析与解决
你遇到的核心问题是固定长度结构体的内存填充:你的struct books里的bookName、authorName都是固定50字节的char数组,输入内容长度远小于50时,数组剩余空间会保留内存中的原始值(可能是\0或随机垃圾字节),用fwrite(&b, sizeof(b), 1, fp)直接写入整个结构体时,这些冗余内容会被一并写入文件。
另外你用的fflush(stdin)是未定义行为,C标准仅允许对输出流使用fflush,对输入流调用的结果由编译器决定,不能依赖它清理缓冲区。gets()已被废弃,改用fgets()是正确方向,但你没处理它读入的换行符,这也可能导致额外问题。
解决方案
方案1:按有效数据长度写入(推荐,适合二进制文件)
不直接写入整个结构体,而是分别写入每个字段的有效内容,同时记录每个字符串的长度(方便后续读取)。修改后的代码如下:
#include<stdio.h> #include<stdlib.h> #include<string.h> struct books { int id; char bookName[50]; char authorName[50]; char date[12]; } b; FILE *fp; void addBook(); void clearInputBuffer(); // 自定义缓冲区清理函数 int main() { int ch; while (1) { system("cls"); printf("<== Library Management System ==>\n"); printf("1. Add Book\n"); printf("0. Exit\n"); printf("Enter your choice: "); scanf("%d", &ch); clearInputBuffer(); // 清理scanf留下的换行符 switch (ch) { case 0: exit(0); case 1: addBook(); break; default: printf("Invalid Choice...\n"); } printf("\nPress Any Key to Continue..."); getch(); } return 0; } // 安全清理输入缓冲区的函数 void clearInputBuffer() { int c; while ((c = getchar()) != '\n' && c != EOF); } void addBook() { fp = fopen("book.dat", "ab"); // 二进制文件建议用.dat后缀区分 if (fp == NULL) { printf("Error opening file!\n"); return; } printf("Enter Book ID: "); scanf("%d", &b.id); clearInputBuffer(); // 清理缓冲区 printf("Enter Book Name: "); fgets(b.bookName, sizeof(b.bookName), stdin); // 去掉fgets读入的换行符 b.bookName[strcspn(b.bookName, "\n")] = '\0'; printf("Enter Author Name: "); fgets(b.authorName, sizeof(b.authorName), stdin); b.authorName[strcspn(b.authorName, "\n")] = '\0'; // 写入ID(固定字节,方便读取) fwrite(&b.id, sizeof(b.id), 1, fp); // 写入书名长度+内容 int nameLen = strlen(b.bookName); fwrite(&nameLen, sizeof(int), 1, fp); fwrite(b.bookName, sizeof(char), nameLen, fp); // 写入作者名长度+内容 int authorLen = strlen(b.authorName); fwrite(&authorLen, sizeof(int), 1, fp); fwrite(b.authorName, sizeof(char), authorLen, fp); fclose(fp); printf("Book Added Successfully.\n"); }
方案2:写入前清空数组(简单但仍有少量冗余)
如果坚持直接写入整个结构体,可以在输入前用memset清空数组,避免垃圾值:
#include<string.h> // 需要引入头文件 void addBook() { fp = fopen("book.txt", "ab"); if (fp == NULL) { printf("Error opening file!\n"); return; } // 清空结构体数组字段 memset(b.bookName, 0, sizeof(b.bookName)); memset(b.authorName, 0, sizeof(b.authorName)); memset(b.date, 0, sizeof(b.date)); printf("Enter Book ID: "); scanf("%d", &b.id); clearInputBuffer(); printf("Enter Book Name: "); fgets(b.bookName, sizeof(b.bookName), stdin); b.bookName[strcspn(b.bookName, "\n")] = '\0'; printf("Enter Author Name: "); fgets(b.authorName, sizeof(b.authorName), stdin); b.authorName[strcspn(b.authorName, "\n")] = '\0'; fwrite(&b, sizeof(b), 1, fp); fclose(fp); printf("Book Added Successfully.\n"); }
这种方式数组剩余部分会被填充为\0,虽比垃圾值好,但仍有冗余字节,适合对文件大小要求不高的场景。
关键注意事项
- 永远不要用
fflush(stdin),用自定义的clearInputBuffer函数清理缓冲区。 - 替换
gets()为fgets(),并处理换行符,避免字符串中包含不必要的换行。 - 二进制文件建议用
.dat这类后缀,和文本文件区分开。 - 后续读取文件时,必须和写入逻辑对应:比如方案1的读取需要先读ID,再读名字长度,最后读对应长度的字符串。
内容的提问来源于stack exchange,提问作者Krish
相关产品推荐
相关产品推荐

