🌟菜鸟主页:@晨非辰的主页
👀学习专栏:《C语言学习》
💪学习阶段:C语言方向初学者
⏳名言欣赏:“人理解迭代,神理解递归。”
目录
4. 文件的顺序读写(续)
4.6 fprintf 函数
int fprintf(FILE* stream, const char* format, ... );
类比printf
int printf (const char* format, ... );
功能:将格式化数据写入指定文件流,与 printf 类似,但可以输出到任意文件(如磁盘文件、标准输出、标准错误等),不限于控制台;
参数:
--stream :指向 FILE 对象的指针,表示要写入的文件流( stdout 、文件指针等);
--format :格式化字符串,含要写入的文本和格式说明符(如 %d 、 %s 等);
--... :可变参数列表,提供与格式字符串中说明符对应的数据;
返回值:--成功,返回写入的字符总数(非负值); --失败,先设置对应流的错误指示器,再返回负值,可以通过 ferror() 来检测。
struct stu
{
char name[30];
int age;
float score;
};
int main()
{
struct stu s = { "zhangsan",18,95.5f };
FILE* fp = fopen("data.txt", "w");
if (fp == NULL)
{
perror("fopen");
return 1;
}
//写文件
fprintf(fp, "%s %d %f", s.name, s.age, s.score);
//关闭文件
fclose(fp);
fp = NULL;
return 0;
}
--将文件输出流换成标准输出流,就和printf一样输出到屏幕
4.7 fscanf 函数
int fscanf(FILE* stream, const char* format, ...);
类比scanf
int scanf (const char* format, ...);
功能:从指定文件流中读取格式化数据。类似 scanf ,但可以指定输入源(如文件、标准输入等),不·限于控制台输入。适用于从文件解析结构化数据(如整数、浮点数、字符串等);
参数:
--stream :指针,表示要读取的文件流(如 stdin 、文件指针等);
--format :格式化字符串,定义如何解析输入数据(如 %d 、 %f 、 %s 等);
--... :可变参数列表,提供存储数据的变量地址(需与格式字符串中的说明符匹配);
返回值:
--成功,函数返回成功填充到参数列表中的项数。该值可能与预期项数⼀致,或因以下原因少于预期(甚至为零):
- 格式和数据匹配失败;
- 读取发生错误;
- 到达文件末尾(EOF)。
-成功读取任何数据之前发生:
- 发生读取错误,在对应流上设置错误指示符,返回 EOF ;
- 到达文件末尾,在对应流上设置文件结束指示符,返回 EOF ;
--在上一函数相关代码运行基础上
struct stu
{
char name[30];
int age;
float score;
};
int main()
{
struct stu s = { 0 };
FILE* fp = fopen("data.txt", "r");
if (fp == NULL)
{
perror("fopen");
return 1;
}
//读文件
fscanf(fp, "%s %d %f", s.name, &(s.age), &(s.score));
fprintf(stdout, "%s %d %f", s.name, s.age, s.score);
//打印数据在stdout
//关闭文件
fclose(fp);
fp = NULL;
return 0;
}
4.8 fwrite 函数
size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream);
功能:将数据从内存缓冲区写入到文件,二进制写入;
参数:
--
ptr
:指向内存缓冲区的指针,缓冲区包含要写入文件的数据;--size:要写入的每个数据元素的大小(字节单位);
--count:要写入数据元素的个数;
--
stream
:指向 FILE 类型结构体的指针,指定要写入数据的文件流。返回值:
--成功:返回成功写入的数据元素个数;
--失败:发生写入错误或到达文件尾,返回值会小于count(
feof()
或ferror()
函数判断)。
--注意:
包含 <stdio.h> 头文件;
使用fwrite() 之前,确保文件以二进制可写方式打开;
fwrite() 通常用于⼆进制数据的写入,如果写入文本数据,请谨慎处理换行符和编码等问题。
--下面以结构体演示:
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { "zhangsan", 20, 95.5 };
FILE*pf = fopen("data.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
if (fwrite(&s, sizeof(struct S), 1, pf) != 1)
{
perror("fwrite");
return -1;
}
fclose(pf);
pf = NULL;
return 0;
}
4.9 fread 函数
size_t fread(void* ptr, size_t size, size_t count, FILE* stream);
功能:从 stream 指向的文件流中读取数据块,并将其存储到 ptr 指向的内存缓冲区中;
参数:
--ptr :指向内存区域的指针,用于存储从文件中读取的数据;
--size :读取的每个数据块的大小(字节单位);
--count :要读取的数据块的数量;
--stream :指向 FILE 类型结构体的指针,指定了要从中读取数据的文件流。
返回值:实际读取的数据块数量。
--注意:
包含 <stdio.h> 头文件;
使用fread() 之前,确保文件已经以二进制可读方式打开;
ptr 指向的内存区域必须足够大,以便存储指定数量和大小的数据块;
fread() 成功读取了指定数量的数据块,返回值等于 count ;读取数量少于count ,可能到达文件结尾或发生错误;
二进制文件读取, fread() 是常用的函数;对于文本文件读取,使用 fgets()或 fscanf()。
#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;
}
--fread 函数与 fwrite 函数的参数作用正好相反,fread 函数是将从data.txt文件中读取到的结构体数据(fwrite函数写入的),存到一个空结构体中。
5. sprintf 、sscanf函数
5.1 相似函数对比
scanf | 针对标准输入(stdin)的格式化输入函数 |
printf | 针对标准输出(stdout)的格式化输出函数 |
fscanf | 针对所有输入流(文件流,stdin)的格式化输入函数 |
fprintf | 针对所有输出流(文件流,stdout)的格式化输出函数 |
sprintf | 将格式化的数据转换成字符串 |
sscanf | 从字符串中提取格式化的数据 |
5.2 sprintf函数
int sprintf(char* str, const char* format, ...);
功能:将格式化数据写入字符数组(字符串)。类似 printf ,但输出目标是用户指定的内存缓冲区。用于动态生成字符串、拼接数据或转换数据格式;(格式化的数据转换为字符串)
参数:
--str:指向字符数组,存储生成的字符串(确保空间够大);
--formate:格式化字符串,定义输出格式(如 %d 、 %f 、 %s 等);
--... :可变参数列表,提供与格式字符串中格式符对应的数据。
返回值:
--成功:写入缓存区的字符数(不包括结尾的空字符 \0 );
--失败;返回负值。
#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);//按照指定的格式打印成字符串形式
return 0;
}
5.3 sscanf函数
int sscanf(const char* str, const char* format, ...);
功能:从字符串中读取格式化数据。与 scanf 类似,但输入源是内存中的字符串。常用于解析字符串中的结构化数据(如提取数字、分割文本等);
参数:
--str:要解析的字符串;
--formate:格式化字符串,定义解析格式(如 %d 、 %f 、 %s 等);
--... :可变参数列表,提供存储数据的变量地址(与格式字符串中的格式符匹配)。
返回值:
--成功:返回成功解析并赋值的参数数量(非负值);
--失败或未匹配任何数据:输入结束或解析失败,返回 EOF (通常是 -1
6. 文件的随机读写
6.1 fseek函数
int fseek( FILE* stream, long int offset, int origin);
功能:根据文件指针的位置和偏移量来定位指针;
参数:
--stream:指向 FILE 类型结构体,指定了要操作的文件流;
--offset:表示偏移量(字节单位)。指定了从origin指定位置开始,文件指针要移动的距离(正负数、0);
--origin:指定偏移量起始位置,整数常量,三种取值情况:
- SEEK_SET:文件开头,offset表示从文件开头开始的偏移量;
- SEEK_CUR:当前文件指针位置,offset是相对于当前位置(光标位置)的偏移量;
- SEEK_END:文件末尾,offset从文件末尾开始的偏移量,为负数用于倒着定位文件指针。
返回值:成功--0;失败--非0(设置errno表示错误类型)
#include<stdio.h>
//先另创建文本文件,内容abcdefghi
int main()
{
FILE* ps = fopen("data1.txt", "r");
if (ps == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(ps);
printf("%c\n", ch);//打印a
//打印g
fseek(ps, 5, SEEK_CUR);
//fgetc函数读取'a',光标向后移到'a'后
//偏移量是5,来到'g'前
ch = fgetc(ps);
printf("%c\n", ch);//g
//关闭文件
fclose(ps);
ps = NULL;
return 0;
}
#include<stdio.h>
int main()
{
FILE* ps = fopen("data1.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;
}
6.2 ftell函数
long int ftell ( FILE * stream );
--介绍
功能:返回文件指针相对于文件起始位置的偏移量;
参数: stream--指向 FILE 类型结构体的指针,指定要操作的文件流,来获取该文件当前的指针位置;
返回值:--成功,返回文件指针相对于文件开头的字节数;v --失败,返回-1L,并设置errno来指示错误类型。
#include<stdio.h>
int main()
{
FILE* ps = fopen("data.txt", "w");
if (ps == NULL)
{
perror("fopen");
return 1;
}
//写文件
fputs("abcdefghi", ps);
//g修改成x,写完后光标在最后
fseek(ps, -3, SEEK_END);
fputc('x', ps);
//光标来到末尾,算相对起始位置的偏移量
fseek(ps, 0, SEEK_END);
//此时光标在末尾,距离于文件开头由有9个字符--9字节
int c = ftell(ps);
printf("%d", c);
//关闭文件
fclose(ps);
ps = NULL;
return 0;
}
6.3 rewind函数
void rewind(FILE* stream );
功能:让文件指针(光标)的位置回到文件起始位置,——>fseek(ps, 0, SEEK_SET);
参数:stream--指向 FILE 类型结构体,指定要操作的文件流,通过参数,rewind 函数能够知道对哪个文件进行操作,将该文件文件指针移至文件开头。
#include<stdio.h>
int main()
{
FILE* ps = fopen("data1.txt", "w");
if (ps == NULL)
{
perror("fopen");
return 1;
}
//写文件
fputs("abcdefghi", ps);
//g修改成x ,光标在末尾
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;
}
7. 文件缓冲区
--ANSI C 标准采用“缓冲文件系统” 处理数据文件,缓冲文件系统是系统自动在内存中为程序每个正使用的文件开辟“文件缓冲区”。从内存向磁盘输出数据先到内存的缓冲区,装满缓冲区后⼀起送到磁盘。若从磁盘向计算机读⼊数据,从磁盘文件中读取数据输入内存缓冲区(充满缓冲区),再从缓冲区逐个将数据送到程序数据区(程序变量等)。缓冲区大小根据C编译系统决定的。
7.1 fflush函数
int fflush(FILE* stream);
功能:强制刷新参数 stream 指定流的缓冲区,确保数据写入底层设备;
- 对输出流:将缓冲区中未写入的数据立即写入文件;
- 对输入流:行为由具体实现决定,非C语言标准行为(可能清空输入缓冲区);
- 参数为 NULL :刷新所有打开的输出流;
参数:stream--指向文件流(stdout 、文件指针等);
返回值:成功返回 0 ,失败返回 EOF;--
--注意
仅对输出流或更新流(最后⼀次操作为输出)有明确刷新行为;
输入流的刷新行为不可移植(如清空输入缓冲区是非标准特性);
程序正常终止( exit )或调用 fclose 时会自动刷新,但程序崩溃时缓冲区数据可能丢失。
#include <stdio.h>
#include <windows.h>
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语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件,如果不做,可能导致读写文件的问题。
8. 更新文件
行为 | "r+" | "w+" | "a+" |
解释 | 可读/可写 | 可读/可写 | 可读/可写 |
文件不存在 | 打开失败 | 创建文件 | 创建文件 |
文件存在 | 保留内容 | 清空 | 保留 |
初始文件指针位置 | 文件开头 | 开头 | 末尾 |
写入是否覆盖数据 | 是(可定位覆盖) | 是(内容清空,从头写) | 否(默认在末尾写数据) |
用途 | 修改文件部分内容 | 创建新文件/重写旧文件 | 在文件末尾追加数据--记录日志 |
注意:
--写完文件后,要继续读文件时,读取前一定使用fflush()刷新缓冲区,或使用fseek()、rewind()重新定位文件指示器位置;
--读完文件后,需要继续写文件前,用fseek()、rewind()重新定位文件指示器的位置。
相关系列回顾:
#C语言——学习攻略:攻克 动态内存分配、柔性数组,根本不在话下!
#C语言——学习攻略:攻克 文件操作内容(一),根本不在话下!
结语:本篇文章到此结束,对于C语言相关知识大家要多次回顾,如果这篇文章对你的学习有帮助的话,欢迎一起讨论学习,你这么帅、这么美给个三连吧~~~