文件追加模式:编写一个程序,向一个已存在的文件末尾追加内容。
知识点
文件打开模式
"r":只读;文件须存在。"w":写入;清空或新建。"a":追加;文件末尾写入。"a+":读/写追加。
追加(Append)机制
"a"模式下,每次写入自动定位到末尾,无须fseek(fp, 0, SEEK_END);安全地在不改变已有数据的前提下增加内容。
行缓冲 I/O
fgets安全读取带换行的一行,防止缓冲区溢出;fputs直接写入字符串,不会再自动添加换行。
命令行参数处理
argc/argv获取用户输入的文件名;fprintf(stderr, …)打印使用说明。
错误处理
检查
fopen、fgets、fputs、fclose的返回值;使用
perror打印系统错误信息。
文件指针自动截断 vs 追加
区分
"w"(截断)和"a"(追加)的不同,用a保留原有数据。
通过本练习,你将掌握文件追加模式的使用方法,以及在实际项目中如何安全、可靠地向已有文件追加新内容。
题目描述
编写一个 C 程序,实现向一个已存在的文本文件末尾追加内容,要求:
程序从命令行接收一个文件名参数;
以追加模式打开该文件;
提示用户输入多行文本,以单独一行只含
.结束;将每行文本追加到文件末尾;
追加完毕后,重新打开文件并显示追加后的全部内容;
全过程需对文件操作的返回值做错误检查,并添加充分注释;
在文末总结本程序涉及的知识点。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define MAX_LEN 1024 //每行最大长度int main(int argc, char *argv[])
{if(argc != 2){fprintf(stderr,"用法:%s <已存在的文件名>\n",argv[0]);return EXIT_FAILURE;}const char *filename = argv[1];char buffer[MAX_LEN];//1.以“追加”模式打开文件//“a”只写追加,目标自动移动到文件末尾//若需要既读又写可用“a”FILE *fp = fopen(filename,"a");if(!fp){perror("打开文件追加失败");return EXIT_FAILURE;}//2.读取用户输入并追加到文件末尾printf("请输入要追加的内容(单独一行“.”结束):\n");while (1){if(!fgets(buffer,sizeof buffer,stdin)){perror("读取标准输入失败");fclose(fp);return EXIT_FAILURE;}//检测结束标志if(strcmp(buffer,".\n") == 0 || strcmp(buffer,".") == 0){break;}//将改行写入文件if(fputs(buffer,fp) == EOF){perror("写入文件失败");fclose(fp);return EXIT_FAILURE;}}//3.关闭追加if(fclose(fp) == EOF){perror("关闭追加流失败");return EXIT_FAILURE;}//4.重新以只读模式打开文件,显示全部内容fp = fopen(filename,"r");if(!fp){perror("打开文件读取失败");return EXIT_FAILURE;}printf("\n-------------追加后的文件内容-----------------\n");while(fgets(buffer,sizeof buffer,fp)){fputs(buffer,stdout);}printf("------------------------------------------------\n");if(fclose(fp) == EOF){perror("关闭读取流失败");return EXIT_FAILURE;}return EXIT_SUCCESS;}

代码讲解
命令行参数
int main(int argc, char *argv[])argc为参数个数,argv[0]是程序名,argv[1]应为目标文件名。if (argc!=2)检查用户是否正确提供了一个参数。
追加模式打开
FILE *fp = fopen(filename, "a");模式
"a":写操作总是从文件末尾开始,不会破坏原有内容;若文件不存在,
"a"会创建新文件(要注意,本题要求文件已存在,可在打开后检查文件是否为空或在打开前用access()验证存在性)。
读取并追加
while (fgets(buffer, sizeof buffer, stdin)) { if (strcmp(buffer, ".\n")==0 || strcmp(buffer,".")==0) break; fputs(buffer, fp); }fgets从标准输入读取一行,保留末尾\n;遇到单独一行
.立刻停止;fputs写入至追加流,无需再fseek——"a"模式自动定位末尾。
关闭流与错误检查
if (fclose(fp) == EOF) perror("关闭追加流失败");fclose会刷新缓冲区并释放资源;始终检查返回值,捕获磁盘 I/O 错误。
重新打开并显示内容
fp = fopen(filename, "r"); while (fgets(buffer, sizeof buffer, fp)) fputs(buffer, stdout);用
"r"模式只读打开;再次用
fgets/fputs按行显示全部文本。
char buffer[MAX_LEN];
fgets(buffer, sizeof buffer, stdin);
功能
从指定的输入流(这里是标准输入stdin)读取一行文本(或最多size–1个字符),并把它存入buffer。如果遇到换行符也会读入并存储在buffer中,最后在末尾自动加上'\0'。原型
char *fgets(char *s, int size, FILE *stream);
参数
char *s—— 目标缓冲区地址,函数会把读到的字符写到s[0]…int size—— 缓冲区大小,最多读入size-1个字符,保证有空间存放终止符'\0'。FILE *stream—— 要读取的文件流,stdin表示标准输入。
返回值
成功时返回
s;遇到 EOF 或发生错误时返回
NULL。
相关/替代函数
fgets(buffer, n, fp):从任意FILE *fp(文件)中读取。不要用已废弃的
gets(不安全,会缓冲区溢出)。如果只想读一个字符,可用
int c = fgetc(stream);。
fputs(buffer, fp);
功能
将以'\0'结尾的字符串buffer写入到指定的输出流fp。不会自动添加换行符。原型
int fputs(const char *s, FILE *stream);
参数
const char *s—— 要写入的 C 字符串的地址。FILE *stream—— 目标文件流,可以是任何通过fopen打开的文件,或标准输出stdout、标准错误stderr。
返回值
写入成功时返回 非负值(通常是字符数或任意非
EOF)。失败时返回
EOF。
相关/替代函数
puts(buffer):等价于fputs(buffer, stdout),并在末尾自动加'\n'。fprintf(fp, "%s", buffer):更灵活,可混合格式化。fwrite(buffer, 1, strlen(buffer), fp):二进制写入。
while (fgets(buffer, sizeof buffer, fp)) {…
}
功能
和第 1 条完全相同,只不过这里stream不是stdin,而是你自己打开的文件流fp。
用它可以逐行读取整个文件。常见用法
FILE *fp = fopen("file.txt", "r"); while (fgets(buffer, sizeof buffer, fp)) {// buffer 中包含一整行文本(包括换行符)process(buffer);//伪代码/示例里的占位符,意思是对刚读入的这一行文本做一些处理 } fclose(fp);其他输入流
fgets(buffer, size, stdin):从键盘读行fgets(buffer, size, pipe_fp):从管道读行fgets(buffer, size, socket_fp)(如果你把 socket 包装成 FILE*)
fputs(buffer, stdout);
功能
把字符串写到标准输出。等同于printf("%s", buffer),但比printf略高效,因为无需解析格式串。常见用法
puts(buffer); // 自动加 '\n'
fputs(buffer, stdout); // 不加 '\n'
相关输出流
fputs(buffer, stderr):输出到标准错误,一般用于打印错误或日志。fputs(buffer, fp):输出到用户打开的任意文件流。
小结
fgets:按行(或按长度)从一个FILE *读字符串,保留'\n'并自动加'\0'。fputs:把一个 C 字符串写入一个FILE *,不附加'\n'。常见流:
输入:
stdin、文件流fp;输出:
stdout、stderr、文件流fp。
替代函数:
输入:
fgetc、getc、gets(已废弃)、getline(POSIX 扩展);输出:
puts、printf、fprintf、fwrite。
掌握这些函数及其参数后,你就能灵活地在控制台、文件或其他 I/O 流之间进行文本读写了。
