一句话理解 fgets
「从文件或输入流中,安全搬运一行‘包裹’(字符串)到指定‘集装箱’(缓冲区),避免超载(溢出)!」
函数原型
#include
char *fgets(char *str, int n, FILE *stream);
入口参数
参数 | 类型 | 比喻解释 |
str | char* | 存放数据的「集装箱」(字符数组) |
n | int | 集装箱的最大容量(含结尾\0) |
stream | FILE* | 货物的「传送带」(文件指针) |
返回参数
返回值 | 含义 |
char* | 成功返回 str 指针 |
NULL | 货物已搬完(文件尾)或搬运失败 |
核心功能图解
传送带上的货物:H e l l o W o r l d
调用 fgets(buf, 10, file) → 第一次搬运:"Hello\n\0",第二次搬运:"World\n\0"
代码实例:安全搬运实战
场景1:从键盘读取用户输入(防溢出)
#include
int main() {
char name[20]; // 集装箱容量:20字符(含\0)
printf("请输入你的名字(最多19字符):");
// 从键盘(stdin)安全搬运输入
if (fgets(name, sizeof(name), stdin) != NULL) {
// 去除末尾可能的换行符
name[strcspn(name, "\n")] = '\0';
printf("你好,%s!\n", name);
} else {
printf("输入失败!\n");
}
return 0;
}
输入测试:
输入:Alice
输出:你好,Alice!
场景2:逐行读取文件内容
#include
int main() {
FILE *file = fopen("diary.txt", "r");
if (!file) {
perror(" 日记本打不开");
return 1;
}
char line[100]; // 集装箱容量:100字符
int line_num = 1;
// 逐行安全搬运,直到文件尾
while (fgets(line, sizeof(line), file) != NULL) {
printf("行%02d:%s", line_num, line);
line_num++;
}
fclose(file);
return 0;
}
文件内容(diary.txt):
2023-10-05 晴
今天学会了fgets函数!
输出:
行01:2023-10-05 晴
行02:今天学会了fgets函数!
常见错误与注意事项
1.未处理换行符
char buf[10];
fgets(buf, 10, stdin);
printf("输入内容:%s", buf); // 若输入"Hello",输出"Hello\n"
// 正确做法:替换换行符(如场景1)
2.误用 sizeof 和 strlen
char buf[20];
// 错误!sizeof(buf)返回20,但若buf是动态分配的指针,会出错
fgets(buf, sizeof(buf), stdin);
// 正确做法(动态数组):
char *buf = malloc(20);
fgets(buf, 20, stdin); // 直接写数字
3.忽略返回值检查
char buf[100];
fgets(buf, 100, file); // 不检查返回值,可能读到NULL
// 正确写法:if (fgets(...)) { ... }
技术细节剖析
1.与 gets 的致命区别
特性 | fgets | gets(已废弃) |
安全性 | 明确指定缓冲区大小 | 可能溢出缓冲区 |
保留换行符 | 保留并存入缓冲区 | 丢弃换行符 |
推荐程度 | 必须使用 | 绝对禁止使用 |
2.精准的字符搬运量
char buf[5];
fgets(buf, 5, file); // 最多搬运4字符+1个\0
- 输入"Hello" → 搬运"Hell\0"(截断剩余字符)
- 输入"Hi" → 搬运"Hi\n\0"
高级技巧:实现 cat 命令
#include
void print_file(FILE *file) {
char buffer[1024];
while (fgets(buffer, sizeof(buffer), file) != NULL) {
fputs(buffer, stdout); // 等效于printf("%s", buffer)
}
}
int main(int argc, char **argv) {
if (argc == 1) {
// 无参数时,从stdin读取
print_file(stdin);
} else {
// 有参数时,逐个打开文件
for (int i = 1; i < argc; i++) {
FILE *file = fopen(argv[i], "r");
if (file) {
print_file(file);
fclose(file);
} else {
fprintf(stderr, "无法打开文件:%s\n", argv[i]);
}
}
}
return 0;
}
总结表格
特性 | 说明 |
安全性 | 防缓冲区溢出 |
换行处理 | 保留换行符并存入缓冲区 |
适用场景 | 逐行读取文件、安全输入 |
性能 | 适合文本处理,不建议二进制流 |
总结
- 核心功能:安全读取字符串,防止缓冲区溢出
- 必用场景:处理用户输入、逐行读取文件、网络数据流
- 类比记忆:就像智能物流机器人,fgets 是程序员安全搬运字符串的「防溢出卫士」