fgets
和 fread
是 C 语言中用于文件读取的两个常用函数,但它们在功能、使用场景和行为上有显著区别。以下是详细对比:
1. 基本定义
函数 | 原型 | 所属头文件 |
|
|
|
|
|
|
2. 核心区别
特性 |
|
|
用途 | 逐行读取文本文件(按行处理) | 按块读取二进制或文本数据(无格式处理) |
终止条件 | 遇到换行符 | 严格读取指定字节数或遇到 EOF |
缓冲区处理 | 自动在末尾添加 | 无终止符,原始数据直接写入内存 |
返回值 | 成功返回 | 返回实际读取的元素数量(非字节数) |
数据类型支持 | 文本数据(字符串) | 二进制或文本数据(无格式限制) |
安全性 | 避免缓冲区溢出(指定最大长度) | 需手动确保缓冲区足够大 |
3. 使用场景
fgets
适用场景
- 读取配置文件、日志文件等文本数据(按行处理)。
- 需要安全地读取一行(避免缓冲区溢出)。
- 示例:逐行解析 CSV 文件。
fread
适用场景
- 读取二进制文件(如图片、音频、结构体数据)。
- 需要高效读取大块数据(如一次读取 1KB 到内存)。
- 示例:复制文件内容或加载序列化数据。
4. 代码示例
(1) fgets
示例:逐行读取文本文件
#include <stdio.h>
int main() {
FILE *file = fopen("text.txt", "r");
if (!file) {
perror("Failed to open file");
return 1;
}
char buffer[256];
while (fgets(buffer, sizeof(buffer), file) != NULL) {
printf("Line: %s", buffer); // 包含换行符
}
fclose(file);
return 0;
}
- 行为:每次读取一行(最多 255 字符 +
\0
),保留换行符。
(2) fread
示例:按块读取二进制文件
#include <stdio.h>
int main() {
FILE *file = fopen("data.bin", "rb");
if (!file) {
perror("Failed to open file");
return 1;
}
unsigned char buffer[1024];
size_t bytes_read;
while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
// 处理二进制数据(如写入到其他文件)
printf("Read %zu bytes\n", bytes_read);
}
fclose(file);
return 0;
}
- 行为:每次读取 1024 字节,不关心内容格式(无终止符)。
5. 关键细节
fgets
的注意事项
(1)换行符处理:
- 如果一行被完整读取,
buffer
末尾会包含\n
。 - 如果缓冲区不足,剩余部分会在下一次调用时读取。
(2)缓冲区大小:
- 参数
n
需预留 1 字节给\0
(如char[256]
则n=256
)。
fread
的注意事项
- 返回值解析:
fread
返回的是成功读取的元素数量(nmemb
),而非字节数。- 若
size=1
,则返回值 = 实际字节数(如示例中的bytes_read
)。
- 二进制模式:
- 必须用
"rb"
模式打开文件,否则在 Windows 上可能因换行符转换出错。
6. 性能与安全性对比
维度 |
|
|
速度 | 较慢(逐字符检查换行符) | 更快(直接内存拷贝) |
安全性 | 高(自动截断超长行) | 需手动检查缓冲区边界 |
灵活性 | 仅限文本 | 支持任意数据类型 |
7. 如何选择?
用 fgets
如果:
- 需要按行处理文本。
- 安全性优先(如用户输入或未知文件)。
用 fread
如果:
- 处理二进制数据(如图片、结构体)。
- 需要高性能读取大文件。
8. 常见问题
Q1: 为什么 fread
读取文本文件时可能出错?
- 原因:若以文本模式(
"r"
)打开文件,fread
可能因系统换行符转换(如 Windows 的\r\n
→\n
)导致字节数不符。 - 解决:始终用
"rb"
模式读取二进制数据。
Q2: fgets
会读取空行吗?
- 会:空行(仅含
\n
)会被读取为"\n\0"
,可通过检查buffer[0] == '\n'
判断。
Q3: fread
能否替代 fgets
?
- 不能直接替代:
fread
无换行符处理逻辑,需手动实现行分割(如查找\n
)。
总结
fgets
:文本行的安全读取工具,适合人类可读的文件。fread
:二进制数据的高效搬运工,适合机器处理的数据块。
根据需求选择,混合使用(如先用 fread
加载到内存,再逐行解析)也是常见优化手段。