标准IO---核心函数接口延续(嵌入式Linux)
继上次讲解标准IO的核心函数接口后,先为大家再补充两个对于二进制文件的读写函数接口,如下:
1、核心函数接口
1.1 fwrite
1.1.1 函数原型
#include // 必须包含的头文件
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
1.1.2 功能
以二进制形式将内存中的数据批量写入指定文件流,不做任何格式转换,是处理二进制文件(如结构体、图片、音频)的核心函数。
关键:
fwrite直接读写内存字节,与数据类型无关,比文本写入(fprintf)更高效、更精准。
1.1.3 参数
| 参数名 | 数据类型 | 含义 |
|---|---|---|
ptr | const void * | 待写入数据的内存起始地址(可以是数组、结构体、普通变量的地址) |
size | size_t | 单个数据块的字节大小(如sizeof(int)、sizeof(Student)) |
nmemb | size_t | 要写入的数据块个数 |
stream | FILE * | 目标文件流指针(需通过fopen以二进制模式打开) |
1.1.4 返回值
size_t:
成功:返回实际写入的完整数据块个数(等于nmemb)
失败:返回小于nmemb的值(可通过ferror(stream)检查错误)
注意:
总写入字节数:成功时总字节数 = size * nmemb
1.2 fread---二进制批量读取文件流
1.2.1 函数原型
#include // 必须包含的头文件
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
1.2.2 功能说明
以二进制形式从指定文件流中批量读取数据到内存,还原为原数据类型(如结构体、数组),是fwrite的配套读取函数。
1.2.3 参数
| 参数名 | 数据类型 | 含义 |
|---|---|---|
ptr | void * | 接收数据的内存起始地址(需提前分配足够空间,如数组、结构体变量) |
size | size_t | 单个数据块的字节大小(需与写入时的size一致) |
nmemb | size_t | 要读取的数据块个数 |
stream | FILE * | 源文件流指针(需通过fopen以二进制模式打开) |
1.2.4 返回值
size_t:
成功:返回实际读取的完整数据块个数(≤ nmemb)读到文件末尾:返回 0
失败:返回小于nmemb的值(可通过ferror(stream)/feof(stream)区分是错误还是到末尾)
1.3 fseek---调整文件指针到指定位置
1.3.1 函数原型
#include
int fseek(FILE *stream, long offset, int whence);
1.3.2 功能
将指定文件流的文件指针移动到「基准位置 + 偏移量」的位置,支持向前 / 向后跳转,是随机读写的核心函数。
1.3.3 参数
| 参数名 | 数据类型 | 含义 |
|---|---|---|
stream | FILE * | 目标文件流指针 |
offset | long | 偏移量(字节数):正数 = 向后跳,负数 = 向前跳,0 = 不跳转 |
whence | int |
基准位置(系统宏定义): • • • |
1.3.4 返回值
| 返回值 | 含义 |
|---|---|
0 | 成功 |
-1 | 失败(如偏移量越界)注意:其他都正确的情况下,超出文件大小仍是成功的 |
注意:
a模式限制:a/a+模式下,fseek调整指针后,写入操作仍会强制跳回文件末尾(仅读取受fseek影响)
1.4 ftell---获取当前文件指针位置(偏移量)
1.4.1 函数原型
#include
long ftell(FILE *stream);
1.4.2 功能
返回指定文件流的偏移量(当前文件指针位置)(相对于文件开头的字节数),常配合fseek使用(如计算文件大小)。
1.4.3 参数
| 参数名 | 数据类型 | 含义 |
|---|---|---|
stream | FILE * | 目标文件流指针 |
1.4.4 返回值
| 返回值类型 | 含义 |
|---|---|
long | 成功:返回指定文件流的偏移量;失败:返回-1L |
1.5 rewind - 重置文件指针到开头
1.5.1 函数原型
#include
void rewind(FILE *stream);
1.5.2 功能
将文件指针重置到文件开头,等价于 fseek(stream, 0, SEEK_SET),但无返回值,更简洁。
1.5.3 参数
| 参数名 | 数据类型 | 含义 |
|---|---|---|
stream | FILE * | 目标文件流指针 |
特点:无返回值,调用即生效
1.6 feof --- 判断是否读到文件末尾
1.6.1 函数原型
#include
int feof(FILE *stream);
1.6.2 功能
检测指定文件流的文件末尾标志(EOF),判断是否已经读取到文件的最后一个字节之后(即 “读完了”)。
关键:
feof不是 “预判” 文件末尾,而是读取操作失败后,判断失败原因是否是 “到了末尾”。
示例:
#include
#include
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) { perror("fopen"); exit(1); }
// 错误写法:先判断feof,会多读取一次(最后一次fgetc返回EOF后,feof(fp)才为非0)
// while (!feof(fp)) { printf("%c", fgetc(fp)); }
// 正确写法:先读取,再判断是否失败,最后用feof找原因
int ch;
while ((ch = fgetc(fp)) != EOF) { // 先读取
printf("%c", ch);
}
// 读取失败后,判断原因
if (feof(fp)) {
printf("
✅ 正常:已读取到文件末尾
");
} else if (ferror(fp)) {
printf("
❌ 错误:读取过程中发生错误
");
}
fclose(fp);
return 0;
}
1.6.3 参数
| 参数名 | 数据类型 | 含义 |
|---|---|---|
stream | FILE * | 目标文件流指针 |
1.6.4 返回值
| 返回值 | 含义 |
|---|---|
| 非 0 值 | 已到达文件末尾(EOF 标志被置位) |
| 0 | 未到达文件末尾 |
1.6 ferror --- 判断文件流是否发生错误
1.6.1 函数原型
#include
int ferror(FILE *stream);
1.6.2 功能
检测指定文件流的错误标志,判断读写操作(如 fputc/fread)是否因硬件 / 权限等问题失败(如读只读文件、写无权限文件)。
1.6.3 参数
| 参数名 | 数据类型 | 含义 |
|---|---|---|
stream | FILE * | 目标文件流指针 |
1.6.4 返回值
| 返回值 | 含义 |
|---|---|
| 非 0 值 | 文件流发生错误(错误标志被置位) |
| 0 | 文件流无错误 |
标志清除:需调用 clearerr(stream) 手动清除错误标志,否则会一直返回非 0。
clearerr(fp) 只做两件事:
- 清除文件指针
fp的结束标志位(EOF); - 清除文件指针
fp的错误标志位(error);它不会改变文件指针的位置,也不会影响文件本身,只是重置fp内部的两个状态标记。
// 必须先清标志 + 重置文件指针,才能重新读
clearerr(fp); // 清除EOF/error标志
fseek(fp, 0, SEEK_SET); // 回到文件开头
1.7 fflush --- 手动刷新文件流缓存
1.7.1 函数原型
#include
int fflush(FILE *stream);
1.7.2 功能
强制将文件流缓存区中未写入的内容刷新到目标设备(文件 / 终端),突破 “缓存满 / 换行 / 程序退出” 的默认刷新规则。
核心场景:实时写入(如日志、监控数据)
1.7.3 参数
| 参数名 | 数据类型 | 含义 |
|---|---|---|
stream | FILE * | 目标文件流指针;传入NULL时,刷新所有打开的文件流缓存 |
1.7.4 返回值
| 返回值 | 含义 |
|---|---|
| 0 | 刷新成功 |
| EOF | 刷新失败 |
适用场景:
- 行缓存:未到
但需要实时输出(如进度条); - 全缓存:文件写入后需要立即落盘(避免程序崩溃丢失数据);
fclose会自动调用fflush:关闭文件前会刷新缓存,无需手动调用。
1.8 setvbuf --- 自定义文件流缓存策略
1.8.1 函数原型
#include
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
1.8.2 功能
在打开文件后、读写操作前,自定义文件流的缓存类型、缓存区地址、缓存区大小,替代系统默认的缓存策略(行缓存 / 全缓存 / 无缓存)。
1.8.3 参数
| 参数名 | 数据类型 | 含义 |
|---|---|---|
stream | FILE * | 目标文件流指针(必须是已打开但未读写的流) |
buf | char * | 自定义缓存区地址:・非 NULL:使用用户指定的缓存区(需提前分配)・NULL:由系统自动分配缓存区 |
mode | int |
缓存模式(系统宏定义): • • • |
size | size_t | 缓存区大小(字节):buf非 NULL 时有效,建议设为 4096/8192 等块大小倍数 |
1.8.4 返回值
| 返回值 | 含义 |
|---|---|
| 0 | 设置成功 |
| 非 0 | 设置失败 |
1.8.5 核心特性
- 调用时机:必须在
fopen之后、第一次读写操作之前调用,否则无效; - 缓存区生命周期:用户自定义的
buf需保证在文件流关闭前有效(避免栈溢出); - 覆盖默认策略:比如将终端流(默认行缓存)改为全缓存,或文件流改为无缓存。
#include
#include
#define BUF_SIZE 8192 // 自定义缓存大小
int main() {
FILE *fp = fopen("test.txt", "w");
if (fp == NULL) { perror("fopen"); exit(1); }
// 分配自定义缓存区(堆内存,避免栈溢出)
char *my_buf = (char *)malloc(BUF_SIZE);
if (my_buf == NULL) { perror("malloc"); fclose(fp); exit(1); }
// 设置为全缓存,使用自定义缓存区
int ret = setvbuf(fp, my_buf, _IOFBF, BUF_SIZE);
if (ret != 0) {
printf("setvbuf failed
");
free(my_buf);
fclose(fp);
exit(1);
}
// 后续读写使用自定义缓存
fputs("自定义缓存测试
", fp);
fflush(fp); // 手动刷新
// 关闭后释放缓存区
fclose(fp);
free(my_buf);
return 0;
}
fflush vs fclose 刷新的区别
| 操作 | 刷新效果 | 后续操作 |
|---|---|---|
fflush(fp) | 仅刷新缓冲区,不关闭文件 | 可继续读写该文件流 |
fclose(fp) | 刷新缓冲区 + 关闭文件 | 不可再操作该文件流(已失效) |
1.9 clearerr --- 清除文件流标志
feof/ferror 的标志被置位后不会自动清除,需用clearerr重置:
#include
void clearerr(FILE *stream);
功能:清除文件流的 EOF 标志和错误标志,恢复初始状态。
错误判断:
feof:读取失败后,判断是否是 “读到末尾”;ferror:读取失败后,判断是否是 “操作出错”;- 核心逻辑:先判断读写是否失败(如
fgetc==EOF),再用这两个函数找原因。
到此,标准IO的内容就到这里了,我们下次再见!











