C语言--文件操作--一起深入了解文件相关函数的知识

news2025/9/14 13:07:13

目录

  • 前言
  • 1.什么是文件
    • 1.1程序文件
    • 1.2数据文件
    • 1.3文件名
  • 2.文件的打开与关闭
    • 2.1文件指针
    • 2.2文件的打开与关闭
      • fopen函数
      • fclose函数
  • 3.文件的顺序读写
    • 3.1对比一组函数
      • fputc函数
      • fgetc函数
      • fputs函数
      • fgets函数
      • fprintf函数
      • fscanf函数
      • fwrite函数
      • fread函数
    • 4.1对比一组函数
      • sprintf
      • sscanf
  • 5.文件的随机读写
    • 5.1fseek
    • 5.2ftell
    • 5.3rewind
  • 6.文本文件和二进制文件
  • 7.文件读取结束的判定
    • 7.1feof函数和ferror函数区别
  • 8.文件缓冲区
  • 总结

前言

在前面学习结构体和动态开辟函数的时候,我们学习通讯录程序的时候,可以给通讯录增加、删除数据,此时数据是存放在内存中的,退出程序的时候数据就不存在了。那怎么样我们才可以将录入通讯录的信息保存下来,使其持久化呢🍭🍭🍭我们平时一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据库等方式。所以我们可以将数据直接存放在电脑的硬盘上,使数据持久化。🐇🐇🐇

1.什么是文件

硬盘上的文件是文件。但是在程序中,我们一般谈的文件有两种:程序文件、数据文件(从功能的角度来分类的)。

1.1程序文件

程序文件包括源程序文件(后缀为.c),目标文件(Windows环境后缀为.obj),可执行程序(Windows环境后缀为.exe)。

1.2数据文件

文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

本章讨论的是数据文件,在以前各章所处理的输入输出都是以终端为对象的,即从终端的“键盘”输入数据,运行结果输出到“显示器”上。其实有时候我们会把信息输出到磁盘上,当需要的时候把数据读取到内存中使用,这里处理的就是磁盘上的文件。

1.3文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。文件包括3部分:文件路径+文件名主干+文件后缀,例如:c:\code\test.txt文件中c:\code为文件路径,test为文件主干,txt为文件后缀。为了方便起见,文件标识常被称为文件名

2.文件的打开与关闭

2.1文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针“。每个被使用的文件都在内存中开辟了一个文件信息区,用来存放文件的相关信息(比如文件的名字、文件的状态、文件的当前位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有类型声明的,该结构体类型被typedef关键字重命名为FILE,该结构体定义包含在收stdio.h的头文件中,因此,使用文件的程序都要包含#include<stdio.h>
在这里插入图片描述

例如,VS2013编译环境提供的stdio.h头文件的文件类型申明:

struct _iobuf {
	char*  _ptr;
	int   _cnt;
	char* _base;
	int   _flag;
	int  _file;
	int  _charbuf;
	int  _bufsiz;
	char* _tmpfname;
};
typedef struct _iobuf FILE;

这是VS2019编译器中FILE类型的定义: 🐽🐽🐽
在这里插入图片描述

不同编译器的FILE类型包含的内容不完全相同,但是大同小异;每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,我们不需要关心细节,一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
下面我们来创建一个FILE的指针变量: 🤓

FILE*pf;//文件指针变量

定义pf是一个指向FILE类型数据的指针变量,可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能访问该信息。也就是说,通过文件指针变量能找到与它关联的文件。✨✨✨
例如:
在这里插入图片描述

2.2文件的打开与关闭

文件在读写之前应该先打开文件,在使用之后应该关闭文件,ANSIC规定打开文件的函数为fopen函数、关闭文件的函数为fclose函数。

fopen函数

在这里插入图片描述
fopen函数原型为

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

头文件为:

#include<stdio.h>

参数介绍:

filename为有效的文件名(即文件的路径),是一个字符串。
mode为文件的访问的方式,有r\r+\rb\rb+\w\w+等模式。

函数介绍:

①打开filename文件,如果打开成功返回返回FILE类型的文件指针,打开文件失败,则返回一个空指针;②返回的文件指针在不需要使用的时候(一般为程序末尾),使用fclose关闭文件。

打开方式为:

文件的打开方式含义
“r”(只读)已输入的方式打开一个文本文件,文件只能读
“r+”(读写)为输入/输出打开一个文本文件
“rb”(只读)已输入的方式打开一个二进制文件,文件只能读
“rb+”(读写)为输入/输出打开一个二进制文件
“w”(只写)以输出方式建立一个文本文件,文件只能写
“w+”(读写)为输入/输出建立一个二进制文件
“wb”(只写)以输出方式建立一个二进制文件,文件只能写
“wb+”(读写)为输入/输出建立一个二进制文件
“a”(追加)向文本文件尾数添加数据,文件只能写
“a+”(读写)为输入/输出打开一个文本文件
“ab”(追加)向二进制文件为添加数据,文件只能写
“ab+”(读写)为输入/输出打开一个二进制文件

fopen函数以“r”、“r+”、“rb“、“rb+”等方式打开文件,文件必须存在,否则fopen()函数将返回NULL;fopen函数以 “w”“w+”“wb”“wb+”等方式打开文件,不要求文件一定存在,若文件不存在则创建该文件,如果文件已经存在,则清空文件原有内容。如果打开操作失败则fopen函数返回空指针(NULL)。fopen函数以“a"“a+”“ab”“ab+”等方式打开文件,不要求文件一定存在,若文件不存在则创建该文件,若文件已经存在则只能在原有数据之后添加内容,如果打开操作失败则fopen函数返回空指针(NULL)。

fclose函数

在这里插入图片描述
fclose的函数原型为:

 int fclose(FILE* stream);

头文件:

#include<stdio.h>

参数介绍:

stream为指向要关闭文件的文件指针

函数介绍:

①将文件指针与文件取消关联(与文件指针相关联的内部缓冲区也取消关联)②写入任何未写入的输出缓冲区的内容,丢弃任何未读入的输入缓冲区的内容,即刷新缓冲区③如果关闭文件成功,则返回零值;如果关闭文件失败,则会返回EOF的值。

fopen函数和fclose函数使用的案例:

#include<stdio.h>
int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读取操作
	//...
	//关闭文件
	fclose(fp);
	fp = NULL;
	return 0;
}

当前存放源文件的位置没有test.txt文件代码运行的结果为:
在这里插入图片描述
在当前源文件添加test.txt文件后代码运行的结果为:
在这里插入图片描述

补充: 在上面的代码中我们使用的都是相对路径,而文件的路径包括相对路径和绝对路径。
相对路径: 如果文件在和源文件同一文件中,文件只需要写文件主干和文件后缀即可,如果该文件在源文件的上一级目录中在前面加上 ..\ (两个点一个斜杠),如果该文件在在源文件的上一级目录的上一级目录中,在前面多加一个..\ ,以此类推。.\ (一个点一个斜杠表示与源文件同级目录,一般可以省略)。
绝对路径: 文件路径+文件名主干+文件后缀。

3.文件的顺序读写

3.1对比一组函数

fputc函数

在这里插入图片描述
fputc函数的原型为:

int fputc(int character, FILE* stream);

头文件:

#include<stdio.h>

参数介绍:

①character为字符,写入文件指针流,进行整型提升,在内部转化为无符号字符;②stream为指向输出流的文件指针。

函数介绍:

①写入成功后,返回所写入的字符,如果写入错误,返回EOF并可以设置ferror(错误指示器)函数进行判断。

fputc函数的使用案例:

#include<stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, pf);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

代码运行的结果为:
在这里插入图片描述

fgetc函数

在这里插入图片描述
fgetc函数的原型为:

int fgetc(FILE* stream);

头文件:

#include<stdio.h>

参数介绍:

stream: 指向标识输入流的FILE对象的指针。

函数介绍:

①从流中获取字符,返回文件指针流当前指向的字符,然后文件流指针自动后移,指向下一个字符。
②如果函数读取成功,将返回读取的字符(提升为int的值)
③如果调用该函数时,指针移至末尾结束,函数返回EOF到文件指针流中,可以设置feof函数进行判断。
④如果发生读取错误而导致程序结束,函数返回EOF设置ferror函数判断。

fgetc函数的使用案例:

//fgetc函数
#include<stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		ch = fgetc(pf);
		printf("%c ", ch);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

代码运行的结果为:
在这里插入图片描述

fputs函数

在这里插入图片描述
fputs函数的原型为:

int fputs(const char* str, FILE * stream);

头文件:

#include<stdio.h>

参数介绍:

①str为文件指针指向的C字符串②stream为FILE类型输出流的指针。

函数介绍:

①写入从str的地址开始写入字符串,一直到终止字符串'\0'②fputs可以指定流进行写入,不会自动写入其他字符,puts只能在标准输出流中写入,会自动添加换行符\n
③如果写入成功,返回非负值,写入失败会返回EOF设置ferror函数进行判断。

fputs函数的使用案例:

#include<stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//测试写一行数据
	fputs("hello wrold\n", pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

代码运行的结果为:
在这里插入图片描述

fgets函数

在这里插入图片描述
fgets的函数原型为:

char* fgets(char* str, int num, FILE* stream);

头文件:

#include<stdio.h>

参数介绍:

①str指向存储字符串的字符数组指针②num为最大的读取个数,包括终止字符'\0'
③stream为指向输入流(文件输入流或标准输入流)的文件指针。

函数介绍:

①从输入流中读取字符到str指向的数组中,直到读取到(num-1)个字符或者遇到换行符或者遇到终止字符停止②换行符使fgets停止读取,并拷贝到str指向的字符串中,终止字符会自动附加在str指向的字符串中③与gets相比,fgets不仅可以指定读取的流,还可以指定读取的最大字符数,而且会自动加上终止字符。

fgets函数的使用案例:

#include<stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//测试读一行数据
	char arr[20] = { 0 };
	fgets( arr,12,pf);
	printf("%s\n", arr);
	fclose(pf);
	pf = NULL;
	return 0;
}

代码运行的结果为:
在这里插入图片描述

fprintf函数

在这里插入图片描述
printf函数原型:

int printf(const char* format, ...);

fprintf函数原型:

int fprintf(FILE* stream, const char* format,...);

参数介绍:

①stream为指向输出流(文件输入流或标准输入流)的文件指针②format为写入的格式

函数介绍:

①将format格式的C字符串写入到输出流中去②如果写入成功,则返回写入的字符总个数,如果写入失败,可以设置ferror函数进行判断并返回负数。

fprintf函数的使用案例:

#include<stdio.h>
struct S
{
	char name[20];
	int age;
	double scores;
};
int main()
{
	struct S s = { "zhangsan",20,98.5 };
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//格式化写入文件
	fprintf(pf, "%s %d %lf", s.name, s.age, s.scores);
	fclose(pf);
	pf = NULL;
	return 0;
}

代码运行的结果为:
在这里插入图片描述

fscanf函数

在这里插入图片描述
scanf函数原型:

int scanf(const char* format, ...);

fscanf函数原型:

int fscanf(FILE*stream,const char* format, ...);

头文件:

#include<stdio.h>

参数介绍:

①stream为指向读取数据的输入流的FILE类型的指针②format为读取的格式

函数介绍:

①从输入流中,按参数格式存储数据到指定地址的变量中去②读取成功后返回参数列表的项目数,也会由于匹配失败、读取错误或遇到文件末尾减少(甚至为零)③如果发生读取错误或读取到文件末尾,可以设置feof函数(ferror函数)进行判断④如果在读取数据之前,匹配失败、读取错误或遇到文件末尾,则会返回EOF。

fscanf函数的使用案例:

#include<stdio.h>
struct S 
{
	char name[20];
	int age;
	double scores;
};
int main()
{
	struct S s = { 0 };
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//格式化读取文件
	fscanf(pf,"%s %d %lf", s.name, &(s.age),&(s.scores));
	printf("%s %d %lf\n", s.name,s.age, s.scores);
	fclose(pf);
	pf = NULL;
	return 0;
}

代码运行的结果为:
在这里插入图片描述

fwrite函数

在这里插入图片描述
fwrite函数原型:

size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream);

头文件:

#include<stdio.h>

参数介绍:

①ptr为指向要写入元素数组的指针②size为每个元素的大小③count为元素的个数④指向输出流的FILE类型的指针。

函数介绍:

①将ptr指向数组的内存块以二进制的形式写入到输出流中去,FILE类型的指针的按总字节数前进②如果写入成功,返回写入的元素总数。如果该数字和count相同,则说明数据成功写入;如果该数字和count不同,则说明函数写入错误,可以设置ferror函数进行判断。

fwrite函数的使用案例:

//测试二进制写入函数
#include<stdio.h>
struct S
{
	char name[20];
	int age;
	double scores;
};
int main()
{
	struct S s = { "张三",20,98.5 };
	FILE* pf = fopen("test.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//以二进制方式写入文件
	fwrite(&s, sizeof(struct S), 1, pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

代码运行的结果为:
在这里插入图片描述

fread函数

在这里插入图片描述
fread函数原型:

size_t fread(void* ptr, size_t size, size_t count, FILE* stream);

头文件:

#include<stdio.h>

参数介绍:

①ptr为指向至少count * size个字节的内存块,转化为void*类型②size为每个元素的大小③count为元素的个数④指向输入流的FILE类型的指针。
函数介绍:
①从输出流中读取数据存储到ptr指向的内存中去,FILE类型的指针的按总字节数前进②如果读取成功,返回读取的元素总数。如果该数字和count相同,则说明数据成功读取;如果该数字和count不同,则说明函数读取错误,可以设置ferror函数进行判断。

fread函数的使用案例:

//测试二进制读取函数
#include<stdio.h>
struct S
{
	char name[20];
	int age;
	double scores;
};
int main()
{
	struct S s = { 0 };
	FILE* pf = fopen("test.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//以二进制方式读取文件
	fread(&s, sizeof(struct S), 1, pf);
	printf("%s %d %lf\n", s.name, s.age, s.scores);
	fclose(pf);
	pf = NULL;
	return 0;
}

代码运行的结果为:
在这里插入图片描述
总结:

功能函数名适用于
字符输出函数fputc所有输出流
字符输入函数fgetc所有输入流
文本行输出函数fputs所有输出流
文本行输入函数fgets所有输入流
格式化输出函数fprintf所有输出流
格式化输入函数fscanf所有输入流
二进制输出函数fwrite文件
二进制输入函数fread文件

1.上面的stream(流)是什么意思呢?首先,我们得知道如果想把数据输出到屏幕、文件、网络等输出设备,由于输出设备不同那么输出方式也不同,会大大增加了程序的复杂度。于是,C语言规定程序中的数据先输入到stream(流)中,再从stream(流)输出到设备中(流到输出设备的方式,已经被封装好了,不需要我们参与),同理,程序读取数据也是从输入流中读取到内存中的。所以进行文件操作的时候,我们需要打开FILE*类型的流,才能进行输入、输出操作。而文件操作中的FILE*类型的流,也是如此。
2.大家有没有想过平时我们写C程序的scanf、printf进行读取的时候,没有打开流就可以进行键盘输入、屏幕输出操作,这是因为任何一个C语言程序运行的时候,默认打开3个流:

  • stdin–标准输入流(键盘),FILE*类型
  • stdio–标准输出流(屏幕),FILE*类型
  • stder–标准错误流(屏幕),FILE*类型
    在这里插入图片描述>3.适用于所有的输入流是指既可以在文件指针流中读取数据到内存,也可以在标准输入流中读取数据到内存中;适用于所有输出流是指既可以把内存中的数据经过文件指针流输出到文件里,也可以从标准输出流输出到屏幕上。

4.1对比一组函数

sprintf

在这里插入图片描述
sprintf函数原型:

int sprintf(char* s, const char* format, ...);

头文件:

#include<stdio.h>

函数介绍:

sprintf函数的使用案例:

#include<stdio.h>
struct S
{
	char name[20];
	int age;
	double scores;
};
int main()
{
	struct S s = { "zhangsan",20,98.5 };
	char arr[100] = { 0 };
	sprintf(arr, "%s %d %lf", s.name, s.age, s.scores);
	printf("%s\n",arr);//按照字符串方式打印
	return 0;
}

代码运行的结果为:
在这里插入图片描述

sscanf

在这里插入图片描述
sscanf函数原型:

int sscanf(const char* s, const char* format,...);

头文件:

#include<stdio.h>

sscanf函数的使用案例:

#include<stdio.h>
struct S
{
	char name[20];
	int age;
	double scores;
};
int main()
{
	struct S s = { "zhangsan",20,98.5 };
	char arr[100] = { 0 };
	sprintf(arr, "%s %d %lf", s.name, s.age, s.scores);
	printf("%s\n",arr);//按照字符串方式打印
	struct S tmp = { 0 };
	sscanf(arr, "%s %d %lf", tmp.name, &(tmp.age), &(tmp.scores));
	printf("%s %d %lf\n", tmp.name, tmp.age, tmp.scores);
	//打印结构体数据
	return 0;
}

代码运行的结果:
在这里插入图片描述
对比scanf/fscanf/sscanf、printf/fprintf/sprintf函数

scanf–从键盘中读取格式化的数据到内存(stdin)
printf–把内存中格式化的数据输出到屏幕上(stdout)
fscanf–针对所有输入流的格式化的输入函数:stdin、文件指针流
fprintf–针对所有输出流的格式化的输出函数:stdout、文件指针流
sscanf–从一个字符串中,还原出格式化的数据。
sprintf–把格式化的数据,转化为(存放在)一个字符串(中)。

5.文件的随机读写

前面介绍的函数,都是进行文件的顺序读写的函数,即文件的指针都是从头到尾移动的,那么有时候文件指针跳过的文件数据需要打印两次,有没有可能使文件指针往回挪呢?接下来我们可以使文件随机读写相关的函数!🐇🐇🐇

5.1fseek

在这里插入图片描述
fseek函数原型:

int fseek(FILE* stream, long int offset, int origin);

头文件:

#include<stdio.h>

参数介绍:

①stream为FILE类型的指针②offset为偏移量③origin为文件指针当前指向的位置

函数介绍:

可以将文件指针设置到任意位置,用于读取重复的数据,如果成功返回零,否则返回非零值

fseek函数的使用案例:

#include<stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch=fgetc(pf);
	printf("%c\n", ch);//a
	ch = fgetc(pf);
	printf("%c\n", ch);//b
	ch = fgetc(pf);
	printf("%c\n", ch);//c
	//接下去读取的是d
	//可以使用fseek函数进行调整读取字符b
	//1.SEEK_SET指针指向文件起始位置
	//fseek(pf, 1, SEEK_SET);
	//ch = fgetc(pf);
	//printf("%c\n", ch);//b
	//2.SEEK_CUR文件指向指向文件当前位置
	//fseek(pf, -2, SEEK_CUR);
	//ch = fgetc(pf);
	//printf("%c\n", ch);//b
	//2.SEEK_END文件指向文件的末尾
	fseek(pf, -5, SEEK_END);
	ch = fgetc(pf);
	printf("%c\n", ch);//b
	fclose(pf);
	pf = NULL;
	return 0;
}

代码运行的结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.2ftell

在这里插入图片描述
ftell函数原型:

long int ftell(FILE* stream);

头文件:

#include<stdio.h>

参数介绍:

stream为FILE类型的指针

函数介绍:

如果函数调用成功,返回FILE类型指针的当前位置,失败则返回-1L。

ftell函数的使用案例:

#include<stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch=fgetc(pf);
	printf("%c\n", ch);//a
	printf("%d\n", ftell(pf));
	ch = fgetc(pf);
	printf("%c\n", ch);//b
	printf("%d\n", ftell(pf));
	ch = fgetc(pf);
	printf("%c\n", ch);//c
	printf("%d\n", ftell(pf));
	fclose(pf);
	pf = NULL;
	return 0;
}

代码运行的结果:
在这里插入图片描述

5.3rewind

在这里插入图片描述
rewind函数原型:

void rewind(FILE* stream);

头文件:

#include<stdio.h>

参数介绍:

stream为FILE类型的指针

函数介绍:

将文件指针重新指向开头,无返回值。
rewind函数的使用案例:

#include <stdio.h>
int main()
{
	int n;
	FILE* pFile;
	char buffer[27];
	pFile = fopen("myfile.txt", "w+");
	for (n = 'A'; n <= 'Z'; n++)
		fputc(n, pFile);
	rewind(pFile);
	//让文件指针重新指向文件的起始位置
	fread(buffer, 1, 26, pFile);
	fclose(pFile);
	buffer[26] = '\0';
	puts(buffer);
	return 0;
}

代码运行的结果:
在这里插入图片描述

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

1.根据数据的组织形式,数据文件被称为文本文件或二进制文件;数据在内存中以二进制的形式存储,如果不加转换输出到外存,就是二进制文件;如果要求在外存上以ASCII码的形式存储,则需要在存储前转换,以ASCII字符的形式存储的文件就是文本文件。
2.一个数据在内存中怎么存储的呢?
字符一律以ASCII形式存储,数值型的数据既可以用ASCII形式存储,也可以使用二进制形存储。

测试代码:

#include<stdio.h>
int main()
{
	int a = 1000;
	FILE* pf = fopen("test.txt", "wb");
	//以二进制形式写到文件
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fwrite(&a, 4, 1, pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

代码调试的过程:
在这里插入图片描述
我们可以发现我们看不懂文件里面的数据,那么接下来我们一起操作一下把!
在这里插入图片描述
添加文件test.txt到源文件中,然后以二进制编辑器方式显示
在这里插入图片描述
在这里插入图片描述
图形理解:
在这里插入图片描述

7.文件读取结束的判定

1.文本文件读取是否结束,判断返回值是否为EOF(fgetc),或者NULL(fgets)
例如:

  • fgetc判断是否为EOF
  • fgets判断返回值是否为NULL
    2.二进制文件的都区结束判断,判断返回值是否小于实际读取要读的个数。
    例如:
  • fread判断返回值是否小于实际要读的个数。

7.1feof函数和ferror函数区别

feof函数
在这里插入图片描述

feof函数原型:

	int feof(FILE * stream);

头文件:

#include<stdio.h>

参数介绍:

stream 为指向标识流的FILE的指针

函数介绍:

①feof函数为真,说明文件正常读取遇到了文件结束指示符而结束②如果设置了文件结束的指示符,返回非零值,否则返回零值。

ferror函数
在这里插入图片描述
ferror函数原型:

	int ferror(FILE * stream);

头文件:

#include<stdio.h>

参数介绍:

stream 为指向标识流的FILE的指针

函数介绍:

f①erro如果返回真,就说明是文件在读取过程中遇到了错误指示符,即出出现错误结束;②如果设置了文件结束的指示符,返回非零值,否则返回零值

文本文件的使用案例:

#include<stdio.h>
int main()
{
	int c = 0;//注意:int.非char,要求处理EOF
	FILE* fp = fopen("test.txt", "r");
	if (!fp)
	{
		perror("File opening failed");
		return 1;
	}
	//fgetc读取失败的时候或者遇到文件结束的时候,都会返回EOF
	while (c = fgetc(fp) != EOF)
	{
		putchar(c);
	}
	//判断是什么原因结束的
	if (ferror(fp))
	{
		puts("I/O error when reading");
	}
	else if (feof(fp))
	{
		puts("End of file reached successful");
	}
	fclose(fp);
	fp = NULL;
	return 0;
}

代码运行的结果:
在这里插入图片描述
二进制文件的例子:

#include<stdio.h>
enum { SIZE = 5 };
int main()
{
	double a[SIZE] = { 1.0,2.0,3.0,4.0,5.0 };
	FILE* fp = fopen("test.bin", "wb");//用二进制写double的数组
	fwrite(a, sizeof(*a), SIZE, fp);
	fclose(fp);
	fp = NULL;
	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 handing
	{
		if (feof(fp))
		{
			printf("Error reading test.bin :unexpected end of file\n");
		}
		else if (ferror(fp))
		{
			perror("Error reading test.bin");
		}
	}
	fclose(fp);
	fp = NULL;
	return 0;
}

代码运行的结果为:
在这里插入图片描述

8.文件缓冲区

ANSIC标准采用“缓冲文件系统”处理数据文件,所谓缓冲系统是系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘中输出数据先送到内存中的缓冲区,等缓冲区装满后再输出到磁盘。如果从磁盘向计算机读入数据,磁盘文件中的数据先输入到内存缓冲区中,等缓冲区装满后再将数据输送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定
在这里插入图片描述
缓冲区刷新例子:

#include<stdio.h>
#include<windows.h>
//在Devc++ WIN11环境测试
int main()
{
	FILE* pf = fopen("test.txt", "w");
	fputs("abcdef", pf);
	printf("睡眠10秒-已经在写数据,打开test.txt文件,发现文件没有内容\n");
	Sleep(1000);
	printf("刷新缓冲区\n");
	fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
	//注意:fflush函数在高版本的VS上不能使用了
	printf("再睡眠10秒-已经在写数据,再打开test.txt文件,文件有内容了\n");
	Sleep(1000);
	fclose(pf);
	//注意:fclose在关闭文件的时候,也会刷新缓冲区
	pf = NULL;
	return 0;
}

ffllush函数刷新之前:
在这里插入图片描述
ffllush函数刷新之后:
在这里插入图片描述

结论: 因为有缓冲区的存在,C语言在操作文件的时候,需要刷新缓冲区或则在文件结束的时候关闭文件;如果不刷新缓冲区,可能导致读写文件的问题。

总结

本章我们一起学习了对文件进行操作的函数以及文件的相关知识,希望对大家了解文件操作又些许帮助,感谢大家阅读!如有不对,欢迎大家纠正!🎠🎠🎠

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/423672.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ClickUp的最佳替代品,可更好地管理项目

ClickUp 是项目管理类的常见工具&#xff0c;它因团队协作、沟通、免费试用和强大功能等因素受到用户的喜爱。 该工具支持你清晰组织多个项目、进行团队协作并跟踪项目目标的实时进度。不同的视图选项使您能够为所有活动构建完美的工作流程。 尽管它很受欢迎&#xff0c;但用户…

ics-05(命令执行漏洞及伪协议读取)

打开链接&#xff0c;似曾相识的感觉&#xff08;前面做过一道题叫ics-06&#xff0c;那道题是对id的一个爆破&#xff09; 尝试后发现只有设备维护中心可以跳转 这和我们使用御剑扫描出来的结果是一样的 使用PHP伪协议读取 index.php 页面 构造payload&#xff08;在愚人杯ht…

小黑跟尚香一起疯狂星期四,然后慢慢跑回家,生活逐渐明朗,完赛了副中心全程马拉松的leetcode之旅:752. 打开转盘锁

宽度优先搜索法 class Solution:def openLock(self, deadends: List[str], target: str) -> int:# 目的地在死亡数组里if target in deadends:return -1# 起点在死亡数字里if 0000 in deadends:return -1# 起点就是目标字符串if 0000 target:return 0# 定义前向函数def pr…

Python与c语言的区别与联系

Python与c语言都是一种机器学习语言&#xff0c;进过长时间的学习和总结&#xff0c;我将Python与c语言的一些特点总结成以下几点&#xff0c;不全面还望多多指正。 1、因为C语言是编译型语言&#xff0c;python是解释型语言&#xff0c;所以python的执行速度没有C语言那么快。…

回归预测 | MATLAB实现GA-GRU遗传算法优化门控循环单元的数据多输入单输出回归预测

回归预测 | MATLAB实现GA-GRU遗传算法优化门控循环单元的数据多输入单输出回归预测 目录回归预测 | MATLAB实现GA-GRU遗传算法优化门控循环单元的数据多输入单输出回归预测效果一览基本介绍程序设计参考资料效果一览 基本介绍 MATLAB实现GA-GRU遗传算法优化门控循环单元的数据多…

详解Nginx代理WordPress搭建个人网站系统

1、搭建环境说明 服务器&#xff1a;Red Hat Enterprise Linux Server release 7.9 (Maipo) PHP&#xff1a;php-8.2.4 PHP: Downloads Nginx: 1.24.0 http://nginx.org/en/download.html WordPress:6.2 Download – WordPress.org Mysql&#xff1a;5.7.22 MySQL Communit…

【Java EE】-网络编程(一) 网络初识

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【JavaEE】 主要内容&#xff1a;单机、局域网、广域网、交换机、路由器。IP地址&#xff0c;端口号&#xff0c;协议&#xff0c;五元组。 协议分层&#xff0c;OSI七层网络模型…

【开源项目】SpringBoot实现接口加密解密

需求背景 在我们日常的Java开发中&#xff0c;免不了和其他系统的业务交互&#xff0c;或者微服务之间的接口调用 如果我们想保证数据传输的安全&#xff0c;对接口出参加密&#xff0c;入参解密。 但是不想写重复代码&#xff0c;我们可以提供一个通用starter&#xff0c;提…

数据结构系列17——lambda表达式

目录 1. 基本概念 2. 基本语法 3. 函数式接口 4. Lambda表达式的基本使用 4.1 语法精简 5. 变量捕获 6. Lambda在集合当中的使用 1. 基本概念 Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和方法一样,它提供了一…

再聊ChatGPT(关于安全,隐私和法律方面的风险)

我在上一篇博文中有谈到ChatGPT 发展可能会经历的一些阶段。ChatGPT的必经阶段&#xff1a;野蛮生长时代-管理层监管与风险提示-号召国产化-规范化常态化。 昨天刚好看到监管部门发文 说明监管部门已经意识到到ChatGPT野蛮生长阶段&#xff0c;其实是存在很多漏洞和问题的。 …

2022年 全国职业院校技能大赛(中职组)网络安全赛项 正式赛卷 A模块 做题记录

评分标准文件及环境 评分标准&#xff1a;ZZ-2022029 网络安全赛项正式赛卷.zip 自己做的Linux靶机&#xff1a; 自己做的Windows靶机&#xff1a; 文章目录评分标准文件及环境A-1 任务一 登录安全加固1. 密码策略&#xff08;Windows&#xff0c;Linux&#xff09;a. 最小密码…

年少不知回损好,却把插损当作宝

一博高速先生成员&#xff1a;黄刚 因为本期要讲的是插损和回损的关系&#xff0c;因此本文的开头&#xff0c;我们还是首先回顾下S参数的概念。首先我们需要知道S参数其实是个黑匣子&#xff0c;什么是黑匣子呢&#xff0c;那就是我们其实不需要知道它包含了哪些链路结构&…

steam游戏搬砖项目怎么做?月入过万的steam搬砖项目教程拆解

steam游戏搬砖项目怎么做?月入过万的steam搬砖项目教程拆解 大家好&#xff0c;我是童话姐姐&#xff0c;今天继续来聊Steam搬砖项目。 Steam搬砖项目也叫CSGO搬砖项目&#xff0c;它并不是什么刚面世的新项目&#xff0c;是已经存在至少七八年的一个资深老牌项目。这个项目…

QT4与QT5兼容问题

QT4 与QT5 兼容&#xff0c;源码差异部分通过QT_VERSION 宏来区分 常见区别 widgets prinsupport charts 等模块一如方式&#xff0c;Qt5 将QtWidgets QtPrintsupport 模块从QtGui 中分离出来&#xff0c;QT4 中没有qjson4 和 charts 模块&#xff0c;需要特殊处理 在pro文件…

SpringSecurity之基础认知

前言 之前一直说开一个SpringSecurity的专栏&#xff0c;今天抽空整理一下&#xff0c;准备开始更新。 也欢迎大家订阅此专栏&#xff01; 什么是SpringSecurity&#xff1f; Spring是非常成功的Java应用框架&#xff0c;目前是非常主流的开发框架。Spring Securtiy正是我们…

RPC 漫谈: 限流问题

RPC 漫谈&#xff1a; 限流问题 微服务之间的 RPC 调用往往会使用到限流功能&#xff0c;但是很多时候我们都是用很简单的限流策略&#xff0c;亦或是工程师拍脑袋定一个限流值。 这篇文章主要讨论在 RPC 限流中&#xff0c;当前存在的问题和可能的解决思路。 为什么需要限流…

Tailwind CSS 小案例,创建漂亮的收藏卡片列表

作为人类&#xff0c;我们有一种天生的倾向&#xff0c;喜欢收集不同的物品&#xff0c;并根据兴趣将它们分组。从邮票到书籍&#xff0c;人们收集和分组的物品种类繁多。定义上&#xff0c;收藏是一组事物&#xff0c;通常是由某个人创建的。例如&#xff0c;很多孩子会收集漫…

Docker In Docker

Docker in Docker 适用场景 ​ 在 CI 中&#xff0c;通常会有一个 CI Engine 负责解析流程&#xff0c;控制整个构建过程&#xff0c;而将真正的构建交给 Agent 去完成。例如&#xff0c;Jenkins 、GitLab 均是如此 同时 Agent 是动态的&#xff0c;构建时才需要&#xff0c;…

查询淘宝商品历史价格(用Python记录商品每天价格变化)

taobao.item_history_price-获取淘宝天猫历史价格接口 思路&#xff1a; 第一步抓取商品的价格存入 Python 自带的 SQLite 数据库每天定时抓取商品价格使用 pyecharts 模块绘制价格折线图&#xff0c;让低价一目了然 接口说明&#xff1a;通过接口可以拿到整个平台&#xff0…

Tomcat源码:Container接口

参考资料&#xff1a; 《Tomcat - Request请求处理: Container设计》 《Tomcat - Container容器之Engine&#xff1a;StandardEngine》 前文&#xff1a; 《Tomcat源码&#xff1a;启动类Bootstrap与Catalina的加载》 《Tomcat源码&#xff1a;容器的生命周期管理与事件监…