fgets
是一个常用的标准库函数,用于从文件或标准输入(如键盘)中读取一行字符串。它的主要特点是安全读取,可以防止缓冲区溢出(buffer overflow),相较于 gets
更安全。
1. fgets
函数原型
char *fgets(char *str, int n, FILE *stream);
参数说明
参数 | 说明 |
| 用于存储读取数据的字符数组(缓冲区)。 |
| 最多读取 |
| 输入流,可以是文件指针(如 |
返回值
- 成功:返回
str
(即传入的缓冲区指针)。 - 失败或到达文件末尾(EOF):返回
NULL
。
2. fgets
的特点
(1)读取一行(直到遇到 \n
或 EOF)
- 如果输入行长度 ≤
n-1
,则读取整行(包括\n
)。 - 如果输入行长度 >
n-1
,则只读取前n-1
个字符(剩余部分留在缓冲区,下次读取)。
(2)自动添加 \0
终止符
- 确保字符串以
\0
结尾,防止越界访问。
(3)不会忽略空格
- 与
scanf("%s", str)
不同,fgets
会读取空格和制表符。
(4)比 gets
更安全
gets
已被弃用(C11标准移除),因为它不检查缓冲区大小,容易导致溢出。
3. 使用示例
示例1:从标准输入(键盘)读取一行
#include <stdio.h>
// 从标准输入(键盘)读取一行
int main()
{
char buffer[100];
printf("please input data:\n");
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
printf("the input is: %s\n", buffer);
}
else {
printf("fgets read fail\n");
}
return 0;
}
运行结果:
please input data:
nimehao
the input is: nimehao
示例2:从文件读取
#include <stdio.h>
#include <string.h> // used for strcmp
// 利用fgets从文件读取数据
int main()
{
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("fopen error");
return 1;
}
char line[100];
printf("size of line is :%zu\n", sizeof(line));
while (fgets(line, sizeof(line), fp) != NULL)
{
// two compare method
// if (strcmp(line, "\n") == 0)
if (line[0] == '\n')
{
printf("empty line\n");
continue;
}
printf("line:%s", line);
}
fclose(fp);
return 0;
}
data.txt
hello, c nihao
I get you.
output:
size of line is :100
line:hello, c nihao
empty line
line:I get you.
4. 关键注意事项
(1) 换行符 \n
的处理
fgets
会保留换行符(如果一行完整读取)。- 检查是否读取了换行符:
if (buffer[strlen(buffer) - 1] == '\n') {
buffer[strlen(buffer) - 1] = '\0'; // 去掉换行符
}
(2) 缓冲区大小 n
的选择
- 必须预留1字节给
\0
:
-
- 如果
char buf[100]
,则n
应设为100
(最多读99字符 +\0
)。
- 如果
- 缓冲区不足时:
-
- 如果一行超过
n-1
字符,剩余部分会在下一次fgets
调用时读取。
- 如果一行超过
(3) 与 scanf
的区别
特性 |
|
|
读取方式 | 按行读取(含空格) | 按格式读取(遇空格停止) |
安全性 | 安全(指定缓冲区大小) | 可能溢出(如 |
换行符 | 保留 | 不保留 |
推荐:优先使用 fgets
+ sscanf
组合,避免 scanf
的安全问题。
fgets
负责安全地读取一行输入(避免 gets
或 scanf
直接读导致的溢出)。
scanf
从 fgets
读取的字符串中提取结构化数据(如整数、浮点数、字符串等)
具体为:
- 用
fgets
读取一行到缓冲区。 - 用
sscanf
从缓冲区中提取数据。 - 检查
sscanf
的返回值,确保解析成功。
示例如下:
#include <stdio.h>
// fgets + sscanf 组合使用
// fgets 负责安全地读取一行输入(避免 gets 或 scanf 直接读导致的溢出)。
// sscanf 从 fgets 读取的字符串中提取结构化数据(如整数、浮点数、字符串等)
int main()
{
char buffer[100];
int num1, num2;
printf("please input two integer(eg:10 20):\n");
if (fgets(buffer, sizeof(buffer), stdin) == NULL)
{
printf("fgets read error\n");
return 1;
}
if (sscanf(buffer, "%d %d", &num1, &num2) != 2)
{
printf("input format error\n");
return 1;
}
printf("the input data is %d and %d\n", num1, num2);
return 0;
}
output:
please input two integer(eg:10 20):
12 334
the input data is 12 and 334
5. 常见问题
Q1: fgets
为什么会读到空行?
- 原因:文件中有空行(仅
\n
),fgets
会读取为"\n\0"
。 - 解决方法:
if (buffer[0] == '\n') {
// 处理空行
}
Q2: fgets
能否读取二进制文件?
- 不推荐:
fgets
是文本行读取函数,遇到\0
会停止。 - 二进制文件应使用
fread
:
fread(buffer, 1, size, file); // 按字节读取
6. 总结
特性 | 说明 |
用途 | 安全读取文本行(文件或标准输入)。 |
换行符处理 | 保留 |
缓冲区安全 | 必须指定最大长度( |
替代 | 是更安全的替代方案。 |
二进制文件 | 不适用,应使用 |
最佳实践:
- 始终检查
fgets
的返回值(是否为NULL
)。 - 处理换行符(
\n
)以避免后续问题。 - 优先使用
fgets
+sscanf
替代scanf
。