2025年文件操作详解

文件操作详解目录 1 文件的基本概念 2 文件的打开和关闭 3 文件的顺序读写 3 1 fputc fgetc 3 2 fputs fgets 3 3 fprintf fscanf 3 4 fwrite fread 3 5 几组函数的对比 4 文件的随机读写 4 1fseek

大家好,我是讯享网,很高兴认识大家。

目录

1.文件的基本概念:

2.文件的打开和关闭

3.文件的顺序读写

3.1:fputc/fgetc

3.2:fputs/fgets

 3.3:fprintf/fscanf

3.4:fwrite/fread

3.5几组函数的对比

4.文件的随机读写

4.1fseek

4.2ftell

4.3rewind

5.文本文件和二进制文件

6.feof的正确使用

 7.文件缓冲区


1.文件的基本概念:

一个文件名包含3部分:文件路径+文件名主干+文件后缀

例:C:\code\test.txt

2.文件的打开和关闭

每一个被打开的文件,在内存中都对应一个文件信息区,该文件信息区存储着该文件的基本属性和内容,这些信息保存在一个名为FILE的结构体变量中的。每次打开一个文件,都会返回该文件在内存中对应的文件指针,通过该指针我们可以修改文件内容。下面介绍文件打开和关闭的函数。

  • FILE * fopen ( const char * filename, const char * mode );

    讯享网
  • 讯享网int fclose ( FILE * stream );

打开文件用fopen,关闭文件用fclose。

打开文件的常用模式:
讯享网

最基本的代码:

int main() { //打开文件 FILE* pf = fopen("data.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 //... //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

3.文件的顺序读写

关于文件顺序读写的函数:

 在讲这些函数之前,我们先说一下”流“的概念。

在编程当中,”流“是一个抽象出来的东西,为什么会存在”流“呢?

这是为了减轻我们程序员的负担。最开始,当我们想要将内存中的内容输出到外部设备(屏幕,硬盘等)时,由于不同的外部设备有着不同的读写方式,导致输出内容时还得知道对应设备的读写方式,这就非常痛苦了。于是,在内存和外部设备之间加入”流“,我们程序员只需要将内容输出到”流“中,至于”流“中的数据怎么到外部设备,由我们的编译器帮我们实现,不用我们去操心了。

那么为什么打开文件时需要我们输入代码,打开什么什么文件,而输出到屏幕上,就不需要了呢?那是因为我们程序运行时,默认启动三个流,分别是:

  • stdin:标准输入流(键盘)
  • stdout:标准输出流(屏幕)
  • stderror:标准错误流

3.1:fputc/fgetc

  • 讯享网int fputc ( int character, FILE * stream );
  • int fgetc ( FILE * stream ); 
讯享网//fputc的用法: //写成功返回字符的ASCII //写失败返回EOF int main() { //打开文件 FILE* pf = fopen("data.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 /*int ch = 'a'; fputc(ch, pf); ch = 'b'; fputc(ch, pf);*/ //进阶的用法 int ch = 0; for (ch = 'a'; ch < 'z'; ch++) fputc(ch, pf); //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

 

//fgetc的用法: //读取成功返回字符ASCII //读取到结尾返回EOF //读取失败返回EOF,并设置错误信息 int main() { //打开文件 FILE* pf = fopen("data.txt", "r");//这次是以读的方式了 if (pf == NULL) { perror("fopen"); return 1; } //操作文件 /*int ch = fgetc(pf); printf("%c ", ch); ch = fgetc(pf); printf("%c ", ch);*/ //进阶的用法 int ch = 0; while ((ch = fgetc(pf)) != EOF) printf("%c ", ch); //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

注意:在读取一个字符后,文件状态指针(可以理解为光标)就自动指向下一个位置 

3.2:fputs/fgets

  • 讯享网int fputs ( const char * str, FILE * stream );
  • char * fgets ( char * str, int num, FILE * stream );

如果说fputc/fgetc是输出/输入一个个字符,那么fputs/fgets就是输出/输入字符串了

讯享网int main() { //打开文件 FILE* pf = fopen("data.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 char arr[10] = "abcdefg"; fputs(arr, pf); //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

int main() { //打开文件 FILE* pf = fopen("data.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 char arr[10] = { 0 }; fgets(arr,10 ,pf);//输出abcdefg printf("%s", arr); fgets(arr, 7, pf); printf("%s", arr);//输出abcdef //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

为什么num为7时只输出了abcdef?因为fgets只输入0~num-1个字符。

 3.3:fprintf/fscanf

  • 讯享网int fprintf ( FILE * stream, const char * format, ... );
  • int fscanf ( FILE * stream, const char * format, ... );

 fprintf/fscanf:输出/输出格式化的数据

讯享网struct S { float f; char c; int n; }; int main() { //打开文件 FILE* pf = fopen("data.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 struct S s = { 3.14f,'a',19 }; fprintf(pf, "%f-%c-%d", s.f, s.c, s.n); //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

struct S { float f; char c; int n; }; int main() { //打开文件 FILE* pf = fopen("data.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 /*struct S s = { 3.14f,'a',19 }; fprintf(pf, "%f-%c-%d", s.f, s.c, s.n);*/ struct S s = { 0 }; fscanf(pf, "%f-%c-%d", &(s.f), &(s.c), &(s.n));//要与写入文件时数据的格式一致 printf("%f-%c-%d\n", s.f, s.c, s.n); //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

3.4:fwrite/fread

前面的几组文件操作函数都是输入输出文本文件的,而fwrite/fread则是输出/输入二进制文件。

  • 讯享网size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
  • size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

ptr指向一个数组,size指数组中每个元素的大小,count指要写/读多少个元素。

fwrite返回成功写入的个数;fread返回成功读取到的个数

讯享网int main() { //打开文件 FILE* pf = fopen("data.txt", "rb"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 /*int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; fwrite(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);*/ //由于写进去的是二进制,我们看不懂,但可以通过二进制读出来进行验证 int arr[10] = { 0 }; fread(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf); for (int i = 0; i < 10; i++) printf("%d ", arr[i]); //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

3.5几组函数的对比

  • printf/fprintf/sprintf
  • scanf/fscanf/sscanf

用一张图来完整表示它们之间的区别

struct S { float f; char c; int n; }; int main() { struct S s1 = { 3.14f,'c',19 }; char arr1[20] = { 0 }; sprintf(arr1, "%f-%c-%d", s1.f, s1.c, s1.n); printf("%s\n", arr1); struct S s2 = { 0 }; char arr2[20] = "3.14-c-19"; sscanf(arr2, "%f-%c-%d", &(s2.f), &(s2.c), &(s2.n));//注意格式要一致 printf("%f-%c-%d", s2.f, s2.c, s2.n); return 0; }

4.文件的随机读写

上面的函数都是在文件中按照顺序进行读写,当我们想读写我们指定的位置,就需要用到随机读写的函数。下面介绍3种。

我们假设文件中的内容为abcdefg

4.1fseek

  • 讯享网int fseek ( FILE * stream, long int offset, int origin );

功能:根据光标的位置和偏移量来定位文件指针 

origin有三种模式:

  • SEEK_SET:文件的最开始
  • SEEK_CUR:当前光标的位置
  • SEEK_END:文件的末尾

offset是偏移量

基本用法:

int main() { //打开文件 FILE* pf = fopen("data.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 int ch = 0; ch = fgetc(pf); printf("%c ", ch);//a ch = fgetc(pf); printf("%c ", ch);//b ch = fgetc(pf); printf("%c ", ch);//c fseek(pf, 0, SEEK_SET); ch = fgetc(pf); printf("%c ", ch);//a fseek(pf, -1, SEEK_CUR); ch = fgetc(pf); printf("%c ", ch);//a fseek(pf, -7, SEEK_END); ch = fgetc(pf); printf("%c ", ch);//a //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

4.2ftell

  • 讯享网long int ftell ( FILE * stream );

功能:返回当前光标相对于起始位置的偏移量 

int main() { //打开文件 FILE* pf = fopen("data.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 int ch = 0; ch = fgetc(pf); printf("%c ", ch);//a ch = fgetc(pf); printf("%c ", ch);//b ch = fgetc(pf); printf("%c ", ch);//c int pos = ftell(pf); printf("%d\n", pos);//3 //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

4.3rewind

  • 讯享网void rewind ( FILE * stream );

 功能:将光标移动到起始位置

int main() { //打开文件 FILE* pf = fopen("data.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //操作文件 int ch = 0; ch = fgetc(pf); printf("%c ", ch);//a ch = fgetc(pf); printf("%c ", ch);//b ch = fgetc(pf); printf("%c ", ch);//c rewind(pf); ch = fgetc(pf); printf("%c ", ch);//a //关闭文件 fclose(pf); pf = NULL;//避免野指针 return 0; }

5.文本文件和二进制文件

数据以ASCII的形式存储到文件中,这时的文件叫做文本文件;

数据以二进制编码的形式存储到文件中,这时的文件叫做二进制文件。

6.feof的正确使用

很多人认为feof是用来判断文件是否读取结束的,这个说法并不是很准确,更明确的讲应该是判断当文件读取结束时,是因为遇到文件末尾而结束,还是因为读取过程中读取失败结束。

文本文件判断读取是否结束:

  • fgetc:返回值是否为EOF
  • fgets:返回值是否为NULL

二进制文件判断读取是否结束:

  • fread:返回值是否小于要求读取到的个数 
讯享网int main() { FILE* pf = fopen("data.txt", "r"); if (!pf) { perror("fopen"); return; } int ch = 0; while ((ch = fgetc(pf)) != EOF) { printf("%c ", ch); } if (ferror(pf)) { printf("I/O erroe when reading\n"); } else if (feof(pf)) { printf("ending of file\n"); } fclose(pf); pf = NULL; return 0; }

 7.文件缓冲区

我们在读取文件或写入数据到文件时,数据并不是直接到文件或内存中的。

实际上,在内存和文件之间有个文件缓冲区,函数执行完数据是放在文件缓冲区当中的,当文件缓冲区满了,文件缓冲区被刷新了或文件关闭了,数据才会加载到我们想要的地方。

可以通过下面的代码来验证:

int main() { FILE* pf = fopen("data.txt", "w"); if (!pf) { perror("fopen"); return; } fputs("Hello file", pf); printf("打开文件,会发现文件没有内容\n"); Sleep(10000); fflush(pf); printf("此时文件有了内容\n"); Sleep(10000); fclose(pf); pf = NULL; return 0; }
小讯
上一篇 2025-03-08 23:03
下一篇 2025-04-01 18:59

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/51042.html