【C语言入门级教学】⽂件的随机读写和文件缓冲区
文章目录
- **点击这里查看文件相关知识的第一篇**(这是第三篇)
- **点击这里查看文件相关知识第二篇**
- 5.11 对⽐几组函数
- 5.11.1 sprintf
- 5.11.2 sscanf
- 5.12总结
- 6.⽂件的随机读写
- 6.1 fseek
- 6.2 ftell
- 6.3 rewind
- 7.⽂件读取结束的判定
- 7.1 被错误使⽤的 feof
- 8.⽂件缓冲区
- 8.1 fflush
- 9.更新文件
点击这里查看文件相关知识的第一篇(这是第三篇)
点击这里查看文件相关知识第二篇
5.11 对⽐几组函数
5.11.1 sprintf
int sprintf(char* str,const char* format,...);
//对比
int printf(const char* format,...);
//将格式化数据打印到屏幕(标准输出)上
功能
将格式化数据写入字符数组(字符串)。它类似于printf,但输出目标不是控制台或文件,而是用户指定的内存缓冲区。常用于动态生成字符串、拼接数据或转换数据格式。简而言之就是将格式化的数据转换成一个字符串
参数
str:指向字符数组的指针,用于存储生成的字符串(需确保足够大以防溢出)
format:格式化字符串,定义输出格式(如%d、%f、%s等)
…:可变参数列表,提供与格式字符串中说明符对应的数据
返回值
成功时返回写入buffer的字符数(不包括结尾的空字符\0)
失败时返回负值
代码演示
#include <stdio.h>
struct S
{char name[20];int age;float score;
};
int main()
{struct S s={"zhangsan",20,95.5f};char buffer[100]={0};sprintf(buffer,"%s %d %f",s.name,s.age,s.score);printf("%s\n",buffer);return 0;
}
5.11.2 sscanf
int sscanf(const char* str,const char* format,...);
int scanf(const char* format,...);
//从键盘上读取格式化的数据,放到变量中
功能
从字符串中读取格式化数据。它与scanf类似,但输入源是内存中的字符串而非控制台或文件。常用于解析字符串中的结构化数据(如提取文字,分割文本等)
参数
str:要解析的源字符串(输入数据来源)
format:格式化字符串,定义如何解析数据(如%d、%f、%s等)
…:可变参数列表,提供存储数据的变量地址(需与格式串中的说明符匹配)
返回值
成功时:返回成功解析并赋值的参数数量(非负值)
失败或未匹配任何数据:若输入结束或解析失败,返回EOF
代码演示
#include <stdio.h>
struct S
{char name[20];int age;float score;
};
int main()
{struct S s={"zhangsan",20,95.5f};char buf[100]={0};sprintf(buf,"%s %d %f",s.name,s.age,s.score);printf("%s\n",buffer);//还原到结构体变量中struct S t={0};sscanf(buf,"%s %d %f",t.name,&(t.age),&(t.score));//格式要对应printf("%s %f %d",t.name,t.score,t.age);//还原到结构体变量中,要按照格式打印return 0;
}
5.12总结
6.⽂件的随机读写
6.1 fseek
int fseek ( FILE * stream, long int offset, int origin );
功能
根据⽂件指针的位置和偏移量来定位⽂件指针(⽂件内容的光标)。
参数
FILE * stream:指向已打开文件的指针,类型为FILE*
。
long int offset:表示移动的字节数,类型为long
。
int origin:起始位置,指定偏移量的基准位置,
常见选项包括:
SEEK_SET
:从文件开头开始计算偏移量。
SEEK_CUR
:从当前位置开始计算偏移量。
SEEK_END
:从文件末尾开始计算偏移量。
返回值
成功时:返回0
,表示文件指针位置已成功调整。
失败时:返回非零值(如-1
),表示操作失败,可能由于文件未打开或参数无效导致。
代码演示
#include <stdio.h>
int main ()
{ FILE * pf= fopen ( "data.txt" , "r" );if(pf==NULL){perror("fopen");return 1;}//读文件int ch=fgetc(pf);//aprintf("%c\n",ch);fseek(pf,6,SEEK_SET);//fseek(pf,5,SEEK_CUR);//fseek(pf,-3,SEEK_END);ch=fgetc(pf);//gprintf("%c\n",ch);fclose(pf);pf=NULL;return 0;
}
data.txt
写文件类似
#include <stdio.h>
int main ()
{ FILE * pf= fopen ( "data.txt" , "r" ); if(pf==NULL) { perror("fopen"); return 1; } fputs("abcdefghi",pf);//写文件 fseek(pf,6,SEEK_SET); //fseek(pf,5,SEEK_CUR); //fseek(pf,-3,SEEK_END); fputc('x',pf);fclose(pf); pf=NULL; return 0;
}
6.2 ftell
long int ftell ( FILE * stream );
功能
用于获取文件指针的当前位置,通常用于二进制文件或文本文件的操作中。该函数返回一个长整型值,表示文件指针相对于文件开头的偏移量。
参数
FILE * stream:指向文件对象的指针,表示需要查询位置的文件流。
返回值
成功时返回当前文件指针的位置(以字节为单位)。
失败时返回-1L
,并设置errno
以指示错误类型。
代码演示
#include <stdio.h>
int main ()
{ FILE * pf= fopen ( "data.txt" , "r" ); if(pf==NULL) { perror("fopen"); return 1; } fputs("abcdefghi",pf); //写文件 fseek(pf,-3,SEEK_END); fputc('x',pf);int ret=ftell(pf);printf("%d\n",ret);fclose(pf); pf=NULL; return 0;
}
//巧妙借助这个函数算出文件内容长度
fseek(pf,0,SEEK_END); int ret=ftell(pf);
printf("%d\n",ret);
6.3 rewind
void rewind ( FILE * stream );
功能
用于将文件指针重新定位到文件的开头,同时清除文件结束标志(EOF)和错误标志。它通常用于需要重新读取文件内容的场景。
参数
FILE *stream
:指向文件对象的指针,表示需要重置位置的文件流。
返回值
rewind
函数没有返回值(返回类型为 void
)。如果操作失败,通常是因为传入的文件指针无效或文件未正确打开,但函数本身不会显式返回错误信息。
代码演示
#include <stdio.h>
int main ()
{ FILE * pf= fopen ( "data.txt" , "r" ); if(pf==NULL) { perror("fopen"); return 1; } fputs("abcdefghi",pf); //写文件 fseek(pf,-3,SEEK_END); fputc('x',pf);fseek(pf,0,SEEK_END);int ret=ftell(pf);printf("文件的字节数%d\n",ret);rewind(pf);//文件指针就回到文件的起始位置了//等价于fseek(pf,0,SEEK_END);fputc('q',pf);fclose(pf); pf=NULL; return 0;
}
结果
7.⽂件读取结束的判定
7.1 被错误使⽤的 feof
牢记:在⽂件读取过程中,不能⽤feof函数的返回值直接来判断⽂件的是否结束。
feof 的作⽤是:当⽂件读取结束的时候,判断是读取结束的原因是否是遇到⽂件尾结束。
1.⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
• fgetc 判断是否为 EOF .
• fgets 判断返回值是否为 NULL .
2.⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。
例如:
• fread判断返回值是否⼩于实际要读的个数。
⽂本⽂件的例⼦:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{int c; // 注意:int,⾮char,要求处理EOFFILE* fp = fopen("test.txt", "r");if(!fp) {perror("File opening failed");return EXIT_FAILURE;}//fgetc 当读取失败的时候或者遇到⽂件结束的时候,都会返回EOFwhile ((c = fgetc(fp)) != EOF) // 标准C I/O读取⽂件循环{putchar(c);}//判断是什么原因结束的if (ferror(fp))puts("I/O error when reading");else if (feof(fp))puts("End of file reached successfully");fclose(fp);
}
⼆进制⽂件的例⼦:
#include <stdio.h>
enum
{ SIZE = 5
};
int main(void)
{double a[SIZE] = {1.,2.,3.,4.,5.};FILE *fp = fopen("test.bin", "wb"); // 必须⽤⼆进制模式fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组fclose(fp);double b[SIZE];fp = fopen("test.bin","rb");size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组if(ret_code == SIZE) {puts("Array read successfully, contents: ");for(int n = 0; n < SIZE; ++n)printf("%f ", b[n]);putchar('\n');} else{ // error handlingif (feof(fp))printf("Error reading test.bin: unexpected end of file\n");else if (ferror(fp)) {perror("Error reading test.bin");}}fclose(fp);
}
8.⽂件缓冲区
ANSIC标准采⽤“缓冲⽂件系统”处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。
从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。
如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的⼤⼩根据C编译系统决定的。
缓冲分为:
1.完全缓冲
2.行缓冲(\n)
3.无缓冲
8.1 fflush
int fflush(FILE* stream);
功能
强行刷新参数stream指定流的缓冲区,确保数据写入底层设备
- 对输出流:将缓冲区中未写入的数据立即写入文件
- 对输入流:行为由具体实现决定,非C语言标准行为(可能清空输入缓冲区)
- 参数为NULL时:刷新所有打开的输出流
参数
stream:指向文件流的指针(如stdout、文件指针等)
返回值
成功返回0,失败返回EOF
注意事项
1.仅对输出流或更新流(最后一次作为输出)有明确刷新行为
2.输入流的刷新行为不可移植(如清空输入缓冲区是非标准特性)
3.程序正常终止(exit)或调用fclose时会自动刷新,但程序崩溃时缓冲区数据可能丢失
代码演示
#include <stdio.h>
#include <windows.h>
//VS2022 WIN11环境测试
int main()
{FILE*pf = fopen("data.txt", "w");fputs("abcdef", pf);//先将代码放在输出缓冲区printf("睡眠10秒-已经写数据了,打开data.txt⽂件,发现⽂件没有内容\n");Sleep(10000);printf("刷新缓冲区\n");fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到⽂件(磁盘)//注:fflush 在⾼版本的VS上不能使⽤了printf("再睡眠10秒-此时,再次打开data.txt⽂件,⽂件有内容了\n");Sleep(10000);//防止和fclose的作用混淆,先在10秒内看到结果,再关闭文件fclose(pf);//注:fclose在关闭⽂件的时候,也会刷新缓冲区pf = NULL;return 0;
}
这⾥可以得出⼀个结论:
因为有缓冲区的存在,C语⾔在操作⽂件的时候,需要做刷新缓冲区或者在⽂件操作结束的时候关闭⽂件。如果不做,可能导致读写⽂件的问题
9.更新文件
关键要点
1.在写完文件后,要继续读文件的时候,在读取之前一定要使用fflush()刷新文件缓冲区,或者使用fseek(),rewind()重新定位文件指示器的位置
2.在读完文件后,需要继续写文件之前,在写文件之前可以使用fseek(),rewind()重新定位文件指示器的位置
#include <stdio.h>
int main ()
{ FILE * pf= fopen ( "text.txt" , "w+" ); if(pf==NULL) { perror("fopen"); return 1; } //写文件fputs("abcdefghi",pf);//文件内容此时在文件缓冲区fflush(pf);//刷新//读文件fseek(pf,0,SEEK_SET);//文件指示器在末尾,要重新定位int ch=fgetc(pf);printf("%c\n",ch);//想继续写文件将文件内容由abcdefghi改为abcxxxghi//文件指示器此时指向bfseek(pf,2,SEEK_CUR);fputs("xxx",pf);fclose(pf);pf=NULL;return 0;
}