
🔥个人主页:@草莓熊Lotso的个人主页 🎬作者简介:C++研发方向学习者 📖个人专栏:《C语言》 ⭐️人生格言:生活是默默的坚持,毅力是永久的享受。
--在上篇文章中,我们学习的函数都是以文本形式的,剩下还有两个函数是以二进制文件,直接看下图。

这里我们继续介绍后面两个函数
1. size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
功能:函数用于将数据块写入stream 指向的文件流中,是以2进制的形式写入的。
参数:
返回值:
返回实际写入的数据项数量。如果发生错误,则返回值可能小于 count 。
使用注意事项:
代码演示:
//fwrite函数演示
//结构体示例
#include<stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
FILE* ps = fopen("data.txt", "wb");
if (ps == NULL)
{
perror("fopen");
return 1;
}
//写文件
struct S s = {"zhaoxiehao",18,85.5f};
if (fwrite(&s,sizeof(struct S),1, ps) != 1)
{
perror("fwrite");
return -1;
}
//关闭文件
fclose(ps);
ps == NULL;//避免变成野指针
return 0;
}
//数组示例
#include<stdio.h>
int main()
{
FILE* ps = fopen("data.txt", "wb");
if (ps == NULL)
{
perror("fopen");
return 1;
}
//写文件
int arr[] = { 1,2,3,4,5 };
if (fwrite(arr,sizeof(int),5, ps) != 5)
{
perror("fwrite");
return -1;
}
//关闭文件
fclose(ps);
ps == NULL;//避免变成野指针
return 0;
}
1. size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
功能:函数用于从 stream 指向的文件流中读取数据块,并将其存储到 ptr 指向的内存缓冲区中。
参数:
返回值:返回实际读取的数据块数量。
使用注意事项:
代码演示:
//fread函数演示
//文件里是之前写出来的结构体信息
#include<stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
FILE* ps = fopen("data.txt", "rb");
if (ps == NULL)
{
perror("fopen");
return 1;
}
//写文件
struct S s = {0};
if (fread(&s, sizeof(struct S), 1, ps) != 1)
{
if (feof(ps))
{
printf("遇到了文件末尾\n");
}
else if (ferror(ps))
{
printf("读取错误");
}
}
else
{
printf("%s %d %f", s.name, s.age, s.score);
}
//关闭文件
fclose(ps);
ps == NULL;//避免变成野指针
return 0;
}能成功读取并打印在屏幕上:

我们通过一个表格来对比着看scanf/fscanf/sscanf,printf/fprintf/sprintf。
scanf | 针对标准输入(stdin)的格式化输入函数 |
|---|---|
printf | 针对标准输出(stdout)的格式化输出函数 |
fscanf | 针对所有输入流(可以是文件流,也可以是stdin)的格式化输入函数 |
fprintf | 针对所有输出流(可以是文件流,也可以是stdout)的格式化输出函数 |
sprintf | 将格式化的数据转换成字符串 |
sscanf | 从字符串中提取格式化的数据 |
前面学习了scanf/fscanf以及printf/fprintf函数,我们再来学习一下sprintf和sscanf函数
1. int sprintf ( char * str, const char * format, ... );
功能:
将格式化数据写入字符数组(字符串)。它类似于 printf ,但输出目标不是控制台或文件,
而是用户指定的内存缓冲区。常用于动态生成字符串、拼接数据或转换数据格式。简而言之就是将格式化的数据转换成一个字符串。
参数:
返回值:
成功时:返回写入 buffer 的字符数(不包括结尾的空字符 \0 )。
失败时:返回负值。
代码演示:
//sprintf函数
#include<stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { "zhaoxiehao",18,85.5f };
char buf[200] = { 0 };
sprintf(buf, "%s %f %d", s.name, s.score, s.age);
printf("%s",buf);//只能按上面sprintf的顺序,打印成字符串形式
return 0;
}
1. i nt sscanf ( const char * str, const char * format, ...);
功能:从字符串中读取格式化数据。它与 scanf 类似,但输入源是内存中的字符串而非控制台或文件。常用于解析字符串中的结构化数据(如提取数字、分割文本等)。
参数:
返回值:
代码演示:
//sscanf函数
#include<stdio.h>
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { "zhaoxiehao",18,85.5f };
char buf[200] = { 0 };
sprintf(buf, "%s %f %d", s.name, s.score, s.age);
printf("%s\n", buf);//只能按上面sprintf的顺序来打印字符串
//这里buf里面已经有了数据了
struct S t = { 0 };
sscanf(buf, "%s %f %d", t.name, &(t.score), &(t.age));
printf("%s %d %f",t.name,t.age,t.score);//这里可以按printf里的顺序打印结构体的数据
return 0;
}我们还可以对比看看两次打印的顺序:

1. int fseek ( FILE * stream, long int offset, int origin );
功能:根据文件指针的位置和偏移量来定位指针
参数:
返回值:
代码演示:
//fseek函数
//在读文件时的运用,假设文件里是abcdefghi
#include<stdio.h>
int main()
{
FILE* ps =fopen("data.txt","r");
if (ps == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(ps);
printf("%c\n", ch);//a
//打印g
//fseek(ps, 6, SEEK_SET);
//fseek(ps, -3, SEEK_END);
fseek(ps, 5, SEEK_CUR);
//这里因为,上面读取字符的时候光标向前
//来到了a,所以偏移量是5
ch = fgetc(ps);
printf("%c\n", ch);//g
//关闭文件
fclose(ps);
ps = NULL;
return 0;
}
//fseek函数
//在写文件时运用
#include<stdio.h>
int main()
{
FILE* ps = fopen("data.txt", "w");
if (ps == NULL)
{
perror("fopen");
return 1;
}
//写文件
fputs("abcdefghi", ps);
//先写不然后面无法执行
//把g修改成x,这里用fputs写完后光标在最后
fseek(ps, -3, SEEK_END);
fputc('x', ps);//写'x'
//关闭文件
fclose(ps);
ps = NULL;
return 0;
}
1. long int ftell ( FILE * stream );
功能:返回文件指针相对于起始位置的偏移量
参数:stream:指向 FILE 类型结构体的指针,指定要操作的文件流,好获取该文件当前的指针位置。
返回值:
代码演示:
//ftell函数
//我们来计算一下文件的字节数
#include<stdio.h>
int main()
{
FILE* ps = fopen("data.txt", "w");
if (ps == NULL)
{
perror("fopen");
return 1;
}
//写文件
fputs("abcdefghi", ps);
//先写不然后面无法执行
//把g修改成x,这里用fputs写完后光标在最后
fseek(ps, -3, SEEK_END);
fputc('x', ps);
//让光标来到最后,再算相对起始位置的偏移量
fseek(ps, 0, SEEK_END);
int c = ftell(ps);
printf("%d", c);
//关闭文件
fclose(ps);
ps = NULL;
return 0;
}
1. void rewind ( FILE * stream );
功能:让文件指针(光标)的位置回到起始位置,等效于fseek(ps, 0, SEEK_SET);
参数:stream:指向 FILE 类型结构体的指针,指定要操作的文件流,通过这个参数,rewind函数能够知道是要对那个文件进行操作,从而将该文件的文件指针移动到文件开头位置
代码演示:
//rewind函数
//返回指针起始位置等效于fseek(ps, 0, SEEK_SET);
#include<stdio.h>
int main()
{
FILE* ps = fopen("data.txt", "w");
if (ps == NULL)
{
perror("fopen");
return 1;
}
//写文件
fputs("abcdefghi", ps);
//先写不然后面无法执行
//把g修改成x ,这里用fputs写完后光标在最后
fseek(ps, -3, SEEK_END);
fputc('x', ps);
//让光标来到最后,再算相对起始位置的偏移量
fseek(ps, 0, SEEK_END);
int c = ftell(ps);
printf("%d", c);
//让文件指针回起始位置,把a改成q;
rewind(ps);
fputc('q', ps);
//关闭文件
fclose(ps);
ps = NULL;
return 0;
}
ANSI C 标准采用“缓冲文件系统” 处理数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每⼀个正在使用的文件开辟⼀块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。

1. int fflush ( FILE * stream );
功能:强制刷新参数 stream 指定流的缓冲区,确保数据写入底层设备。
参数: stream :指向文件流的指针(如 stdout 、文件指针等)
返回值:成功返回 0 ,失败返回 EOF
注意事项:
代码演示:
//fflush
//冲刷缓冲区
#include <stdio.h>
#include <windows.h>
//VS2022 WIN11环境测试
int main()
{
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
//注:fflush 在高版本的VS上不能使用了
printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
return 0;
}
刷新缓冲区之前与之后的文件变化如下:

这里可以得出一个结论:
因为存在缓冲区,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件,如果不做,可能导致读写文件的问题。
--在文件的打开模式中有三种方式值得注意,分别是:"r+","w+","a+",它们分别是啥意思呢?
行为 | "r+" | "w+" | "a+" |
|---|---|---|---|
解释 | 可读/可写 | 可读/可写 | 可读/可写 |
文件不存在时 | 打开失败 | 自动创建新文件 | 自动创建新文件 |
文件存在时 | 保留内容 | 清空内容 | 保留内容 |
初始文件指针位置 | 文件开头 | 文件开头 | 文件末尾 |
写入是否覆盖原有数据 | 是(可定位覆盖) | 是(内容已清空,从头写入) | 否(默认是在文件末尾写数据) |
典型用途 | 修改文件部分内容 | 创建新文件或完全重写旧文件 | 在文件末尾追加数据,比如记录日志 |
补充:"w"和"wb"这类的在文件存在时,也是先清空内容,再创建的新文件
关键要点:
代码演示:
////更新文件
int main()
{
FILE* pf = fopen("test.txt", "w+");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
fputs("abcdefghi", pf);
fflush(pf);//刷新缓冲区
//读文件
//先回到初始位置
fseek(pf, 0, SEEK_SET);
//rewind(pf);也可以
int ch = fgetc(pf);
printf("%c\n", ch);//a
//abcdefghi,把def改成xxx
//上面读完一个后,光标来到a后面,也就是b前面
//离d的偏移量是2;
fseek(pf, 2, SEEK_CUR);
fputs("xxx", pf);
//abcxxxghi
fclose(pf);
pf = NULL;
return 0;
}
往期回顾:
【通关文件操作(上)】--文件的意义和概念,二进制文件和文本文件,文件的打开和关闭,文件的顺序读写
【C语言动态内存管理】--动态内存分配的意义,malloc和free,calloc和realloc,常见的动态内存的错误,动态内存经典笔试题分析,柔性数组,总结C/C++中程序内存区域划分
结语:本篇文章就到此结束了,继前面一篇文章后,在此篇文章中给大家分享了文件操作中的剩余部分,如文件的顺序读写(续),sprintf和sscanf函数,文件的随机读写,文件缓冲区,更新文件等知识点,后续会继续分享其它的内容,如果文章对你有帮助的话,欢迎评论,点赞,收藏加关注,感谢大家的支持。