C语言 fgets 函数详解
fgets 是 C 标准库 <stdio.h> 中一个非常重要且常用的函数,用于从指定的流(如标准输入 stdin、文件等)中读取一行字符串。

函数原型
char *fgets(char *str, int n, FILE *stream);
参数详解
fgets 函数接受三个参数:
-
*`char str`**:
- 这是一个字符指针(或字符数组的首地址)。
- 它的作用是存储从流中读取到的内容。
- 你需要预先分配足够的内存空间来存放读取到的字符串,如果使用数组,数组的大小必须足够大。
-
int n:- 这是一个整数,表示最多要读取的字符数(包括结尾的空字符
\0)。 - 这是
fgets一个非常关键的安全特性,可以有效防止缓冲区溢出。 fgets最多会读取n-1个字符,然后在末尾自动添加一个空字符\0来构成一个合法的 C 字符串。
- 这是一个整数,表示最多要读取的字符数(包括结尾的空字符
-
*`FILE stream`**:
(图片来源网络,侵删)- 这是一个指向
FILE对象的指针,代表了要读取的输入流。 - 可以是标准输入流
stdin(代表键盘输入),也可以是通过fopen函数打开的文件指针。 stdin或fp(FILE *fp = fopen("test.txt", "r");)。
- 这是一个指向
返回值
fgets 函数的返回值是一个指针,有三种情况:
- 成功: 返回
str,即指向存储读取内容的缓冲区的首地址。 - 遇到文件结束符: 如果在读取任何字符之前就到达了流的末尾(从文件末尾读取或用户按下了
Ctrl+D/Ctrl+Z),则返回NULL指针。 - 读取出错: 如果在读取过程中发生错误(读取文件时发生 I/O 错误),也会返回
NULL指针。
重要提示: 当
fgets返回NULL时,你需要通过检查feof(stream)或ferror(stream)来区分是“到达文件末尾”还是“发生错误”。
工作原理
fgets 的工作流程如下:
- 从
stream指定的流中读取字符。 - 读取过程在以下三种情况下会停止:
- 已经读取了
n-1个字符。 - 读取到了换行符
\n,换行符\n会被包含在读取到的字符串中。 - 到达了流的末尾(EOF)。
- 已经读取了
- 无论在哪种情况下停止,
fgets都会在读取到的所有字符的末尾自动添加一个空字符\0,使其成为一个标准的 C 语言字符串。
从标准输入 stdin 读取(键盘输入)
这是 fgets 最常见的用途之一,用于安全地获取用户输入的一行字符串。

示例1:基本用法
#include <stdio.h>
#define BUFFER_SIZE 100
int main() {
char name[BUFFER_SIZE];
printf("请输入您的名字: ");
// 从标准输入读取最多 BUFFER_SIZE-1 个字符,存入 name 数组
if (fgets(name, BUFFER_SIZE, stdin) != NULL) {
// 成功读取
printf("您好, %s", name); // 注意:name 中包含了换行符 \n
} else {
// 读取失败或遇到 EOF
printf("输入发生错误或已结束,\n");
}
return 0;
}
运行示例:
请输入您的名字: 张三
您好, 张三
分析:
用户输入 "张三" 并按下回车。fgets 读取了 "张三" 和回车符 \n,总共 4 个字符(在中文环境下,"张三"算两个字符,加上 \n 共3个),然后在末尾加上 \0。name 数组的内容是 {'张', '三', '\n', '\0'}。printf 输出时,%s 会一直打印到 \0,\n 也会被输出,导致光标换到了下一行。
示例2:处理换行符
在很多情况下,我们不希望字符串末尾包含换行符,我们可以手动将它去掉。
#include <stdio.h>
#include <string.h> // 用于 strlen 函数
#define BUFFER_SIZE 100
int main() {
char city[BUFFER_SIZE];
printf("请输入您所在的城市: ");
if (fgets(city, BUFFER_SIZE, stdin) != NULL) {
// 检查最后一个字符是否是换行符
size_t len = strlen(city);
if (len > 0 && city[len - 1] == '\n') {
// 如果是,则将其替换为字符串结束符 \0
city[len - 1] = '\0';
}
printf("您来自: %s\n", city); // 现在输出不会多一个空行了
}
return 0;
}
运行示例:
请输入您所在的城市: 北京
您来自: 北京
分析:
strlen(city) 返回字符串长度(不包括 \0),如果最后一个字符是 \n,我们就用 city[len - 1] = '\0'; 将它覆盖掉,字符串就在 \n 的位置提前结束了。
从文件中读取
fgets 同样非常适合逐行读取文本文件。
示例3:逐行读取文件
假设我们有一个名为 poem.txt 的文件,内容如下:
床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。
读取该文件的代码:
#include <stdio.h>
#define LINE_LENGTH 256
int main() {
FILE *fp;
char line[LINE_LENGTH];
// 以只读方式打开文件
fp = fopen("poem.txt", "r");
if (fp == NULL) {
perror("无法打开文件 poem.txt");
return 1;
}
printf("文件内容如下:\n");
printf("--------------------\n");
// 循环读取每一行,直到 fgets 返回 NULL
while (fgets(line, LINE_LENGTH, fp) != NULL) {
// 去掉行尾的换行符
size_t len = strlen(line);
if (len > 0 && line[len - 1] == '\n') {
line[len - 1] = '\0';
}
printf("%s\n", line);
}
// 关闭文件
fclose(fp);
return 0;
}
运行结果:
--------------------
床前明月光
疑是地上霜
举头望明月
低头思故乡
分析:
while (fgets(line, LINE_LENGTH, fp) != NULL) 是逐行读取文件的经典模式,循环会一直持续,直到 fgets 到达文件末尾并返回 NULL,此时循环自动终止。
fgets 与 gets 的对比
在 C11 标准之前,还有一个函数叫 gets,但它极其危险,已经被标准弃用,并在 C11 中移除。
| 特性 | fgets |
gets (已弃用) |
|---|---|---|
| 安全性 | 安全,可以指定最大读取长度,防止缓冲区溢出。 | 极不安全,无法指定读取长度,总是读取直到遇到换行符,极易导致缓冲区溢出。 |
| 参数 | 3个:缓冲区地址、最大长度、输入流 | 1个:缓冲区地址 |
| 换行符 | 保留换行符 \n 在读取的字符串中。 |
丢弃换行符 \n。 |
| 输入流 | 可以是 stdin、文件等任意流。 |
只能是标准输入 stdin。 |
永远不要使用 gets 函数!请始终使用 fgets 来替代它。
fgets 是 C 语言中处理字符串输入的基石,无论是从键盘还是从文件读取,它都是首选函数。
-
优点:
- 安全可控: 通过
n参数防止缓冲区溢出。 - 功能强大: 既能从标准输入读取,也能从文件读取。
- 行为明确: 总是以
\0并保留换行符。
- 安全可控: 通过
-
注意事项:
- 记得检查返回值是否为
NULL,以处理错误或文件结束的情况。 - 如果不希望字符串末尾有换行符,需要手动去除。
- 使用前,确保目标缓冲区(
str)有足够的空间。
- 记得检查返回值是否为
掌握 fgets 是编写健壮、安全 C 程序的重要一步。
