文件操作【详解】

文件操作【详解】_第1张图片

目录

一、什么是文件

二、文件的打开和关闭

1.文件指针

2.文件的打开和关闭

3.文件的打开方式

三、文件的顺序读写

1.关于输入输出,读和写

2.关于流的介绍

3.操作文件的函数

 字符输入函数 fgetc的使用

 字符输入函数 fputc的使用

 文本行输出函数  fputs()

文本行输入函数 fgets

格式化输出函数 fprintf()

格式化输入函数 fscanf() 

二进制输出 fwrite

二进制输入 fread (从文件到内存)

四、文件的随机读写 

 1.fseek

2.ftell 

3.rewind 

 五、文本文件和二进制文件

六、文件读取结束的判定 

七、文件缓冲区 


前言:当初在通讯录上的数据都是临时存放到内存中的,当程序运行结束的时候,所添加的数据就没有了。等到下一次运行通讯录的时候,数据又需要重新录入,我们发现这样会很繁琐,我们就想着用什么来存着这些数据一直保留着。

于是 这个问题就涉及到数据持久化的问题了,我们一般数据持久化的方法有:数据存放到磁盘文件中、存放到数据库中等方式。所以这里就提到了文件

一、什么是文件

平时我们说的硬盘上的文件就文件

当然在程序设计中,我们一般说到的文件有两种:程序文件、数据文件(从文件功能上来看)。

1.程序文件

  •  源程序文件 (后缀 .c)
  • 目标文件(.obj)
  • 可执行程序(.exe)

2.数据文件

程序运行时读写的数据,需要输入输出的文件

3. 文件名

文件名包含三部分: 文件路径+文件名主干+文件后缀

例如 d:\code\test.txt

二、文件的打开和关闭

1.文件指针

在缓冲文件系统中,关键的概念是 文件类型指针 ,简称 文件指针

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息,如(文件的名字,文件状态及文件当前的位置等),这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE

每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节

创建一个FILE*的指针变量

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

定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件

文件操作【详解】_第2张图片

2.文件的打开和关闭

文件在读写之前应该先打开文件,在使用结束之后应该关闭文件

在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系

对于打开文件我们一般使用的是 fopen()函数,然后使用fclose()函数来关闭文件

//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );

文件操作【详解】_第3张图片

注意当直接写文件主干时,文件是用相对路径来存放的 

3.文件的打开方式

 文件操作【详解】_第4张图片

还有关于二进制文件

"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
文件操作【详解】_第5张图片

三、文件的顺序读写

按顺序进行文件的读写

1.关于输入输出,读和写

首先这是一个相对而言的,例如 相对于一个人而言 读课本是输入(就比如知识存到大脑),然后 写博客就是 输出(把大脑里面的知识输出到博客文章中)。

文件操作【详解】_第6张图片

2.关于流的介绍

这是有些人就有一些疑问,当我们是printf , scanf 又是怎么一回事呢?

通常把显示器称为标准输出文件 printf() 就向这个文件输出数据

通常把键盘称为标准输入文件 scanf() 就向这个文件读取数据

c 语言运行起来 默认打开 三个流 

  • 标准输入流stdin 
  • 标准输入流stdout
  • 标准错误流stderr

所以我们要记得关闭文件

流  是一个抽象的概念 

IO文件流 : 输入流:数据从文件复制到内存的过程; 输出流:数据从内存保存到文件的过程。

在IO文件流中,是相对于计算机程序中的内存来说的 

文件操作【详解】_第7张图片

3.操作文件的函数

文件操作【详解】_第8张图片

 字符输入函数 fgetc的使用

int fgetc ( FILE * stream );

这里的 int 返回值 会接收到的内容,当遇到文件末尾,读取失败会返回EOF

#include
int main()
{
	//使用fopen()打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch = fgetc(pf);
	printf("%c\n",ch);
	ch = fgetc(pf);
	printf("%c\n", ch);
	//使用fclose()关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

使用stdin  从键盘上读

#include
int main()
{
	//使用fopen()打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch = fgetc(stdin);
	printf("%c\n", ch);//从键盘上读
	ch = fgetc(stdin);
	printf("%c\n", ch);
	//使用fclose()关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

 文件操作【详解】_第9张图片

 字符输入函数 fputc的使用

int fputc ( int character, FILE * stream );

#include
int main()
{
	//使用fopen()打开文件
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件,写一个字符放到流中
	fputc('a',pf);
	fputc('c',pf);
	//使用fclose()关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

打开文件查看

文件操作【详解】_第10张图片

之前介绍的stdout 也可以不写入到文中,写入到屏幕上

#include
int main()
{
	//使用fopen()打开文件
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件,写一个字符放到流中
	//stdout 写入到屏幕上
	fputc('a', stdout);
	fputc('c', stdout);
	//使用fclose()关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

 上述是文件的顺序读写

 文本行输出函数  fputs()

从程序输出到文件中(写)

int fputs ( const char * str, FILE * stream );
#include
int main() 
{
	FILE* pf = fopen("data.txt","w");
	if (pf == NULL) 
	{
		perror("fopen");
		return 1;
	}
	//写文件  一行的写
	fputs("hello\n",pf);
	fputs("world\n",pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

文件操作【详解】_第11张图片

文本行输入函数 fgets

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

要木遇到最多读n - 1 个,后面再追加一个'\0',要木遇到\n 不再读了

//一行的读
#include
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件  一行的读
	char arr[10] = {0};
	fgets(arr,10,pf); //这里只读了9个. 要木遇到最多读n - 1 个,后面再追加一个'\0',要木遇到\n 不再读了
	printf("%s",arr);
	fclose(pf);
	pf = NULL;
	return 0;
}

格式化输出函数 fprintf()

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

Write formatted data to stream  写格式化数据到流里面去

#include
struct S 
{
	int a;
	float b;
};
int main() 
{
	FILE* pf = fopen("data.txt","w");
	if (pf == NULL) 
	{
		perror("fopen");
		return 1;
	}

	//写文件 从程序中写入文件
	struct S s = {100,3.14f};
	fprintf(pf,"%d %f",s.a,s.b);

	fclose(pf);
	pf = NULL;
	return 0;
}

文件操作【详解】_第12张图片

格式化输入函数 fscanf() 

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

Read formatted data from stream  把带有格式的数据从流里面读出

#include
struct S
{
	int a;
	float b;
};
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件 把文件里面的数据读到程序里面去
	struct S s = {0};
	fscanf(pf,"%d %f",&(s.a),&(s.b));
	//这里把数据打印出来
	printf("%d %f",s.a,s.b);
	//这里把数据打印出来的第二种方式
	//fprintf(stdout,"%d %f",s.a,s.b);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

区分:

文件操作【详解】_第13张图片

// sprintf  和  sscanf  

#include
struct S
{
	int a;
	float b;
	char str[10];
};
int main()
{
	char arr[30] = { 0 };
	struct S s = { 100,3.14f,"hello" };//将结构体里面的数据转换为字符串arr里面
	struct S tmp = {0};
	sprintf(arr, "%d %f   %s", s.a, s.b, s.str);//里面的空格也会转换到arr里面

	//从字符串中拿出格式化数据
	sscanf(arr,"%d %f %s",&(tmp.a),&(tmp.b),tmp.str);//从字符串arr中读取格式化数据
	printf("%d %f %s", tmp.a,tmp.b,tmp.str);
	return 0;
}

二进制输出 fwrite

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

文件操作【详解】_第14张图片

#include
struct S 
{
	int a;
	float b;
	char str[10];
};
int main() 
{
	struct S s = {100,3.14f,"zz"};
	FILE* pf = fopen("data.txt","wb");
	if (pf == NULL) 
	{
		perror("fopen");
		return 1;
	}

	//写文件
	fwrite(&s,sizeof(struct S),1,pf);

	fclose(pf);
	pf = NULL;
	return 0;
}

 打开文件 我们发现 看不明白(因为是二进制文件)

文件操作【详解】_第15张图片

 可以使用fread 来读二进制文件

二进制输入 fread (从文件到内存)

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

Read block of data from stream 

//fread
#include
struct S
{
	int a;
	float b;
	char str[10];
};
int main()
{
	struct S s = { 0 };
	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读文件
	fread(&s,sizeof(struct S),1,pf);
	//打印
	printf("%d %f %s",s.a,s.b,s.str);
	fclose(pf);
	pf = NULL;
	return 0;
}

四、文件的随机读写 

这里的文件随机读写 是 想读写哪里就读写哪里

 1.fseek

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

根据文件指针的位置 和 偏移量来定位文件指针

文件操作【详解】_第16张图片

注意origin的参数

文件操作【详解】_第17张图片

  •  SEEK_SET  从文件的起始位置开始
  •  SEEK_CUR   从当前所指向的位置开始
  •  SEEK_END   从文件的末尾开始  (注意从右向左 数字为负的)
//fseek的使用
#include
int main() 
{
	FILE* pf = fopen("data.txt","r");
	//文件里面的内容为abcdefghi
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fseek(pf,3,SEEK_SET);//定位文件指针的指向,这里的正3表示从左到右
	int ch = fgetc(pf);
	printf("%c\n",ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

图解 

文件操作【详解】_第18张图片

2.ftell 

返回文件指针相对于起始位置的偏移量 

long int ftell ( FILE * stream );

//ftell返回文件指针相对于起始位置的偏移量
#include
int main() 
{
	FILE* pf = fopen("data.txt","r");
	//文件内容是abcdefghi
	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
	//用ret来接收偏移量
	int ret = ftell(pf);
	printf("%d\n",ret);//3  这里只适用于标准输出流(屏幕)
	fprintf(stdout,"%d",ret);//3 适用于所有的输出流
	fclose(pf);
	pf = NULL;
	return 0;
}

文件操作【详解】_第19张图片

3.rewind 

让文件指针的位置 回到 文件的起始位置

void rewind ( FILE * stream );
//rewind 让文件指针的位置 回到 文件的起始位置
#include
int main()
{
	FILE* pf = fopen("data.txt", "r");
	//文件内容是abcdefghi
	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
	//用ret来接收偏移量
	int ret = ftell(pf);
	printf("%d\n", ret);//3  这里只适用于标准输出流(屏幕)

	//使用rewind 回到起始位置
	rewind(pf);
	int a = ftell(pf);// 0
	fprintf(stdout,"%d",a);

	fclose(pf);
	pf = NULL;
	return 0;
}

文件操作【详解】_第20张图片

 五、文本文件和二进制文件

文本文件 

如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件

二级制文件

数据在内存中以二进制的形式存储,如果不加转换的输出到外存 

例如 十进制的10000 看下方图解

文件操作【详解】_第21张图片

 例如

#include
int main() 
{
	int a = 10000;
	FILE* pf = fopen("data.txt","wb");
	if (pf == NULL) 
	{
		perror("fopen");
		return 1;
	}
	fwrite(&a,5,1,pf);//写到二进制文件中
	fclose(pf);
	pf = NULL;
	return 0;
}

我们已经把数据以二进制形式写入到文件中,但是当我们打开时发现

文件操作【详解】_第22张图片

这是二进制文件,我们需要用特殊的编辑器来查看,就比如使用 Visual Studio 2022 来进行查看

下面是查看方法:

第一步  先 右击  源文件 - 添加 - 现有项

文件操作【详解】_第23张图片

第二步 找到文本并添加进去

文件操作【详解】_第24张图片

 这时就会看到

第三步   右击 文件 - 点击打开方式  

文件操作【详解】_第25张图片

第四步 选择二进制编辑器

文件操作【详解】_第26张图片

然后就有了 

文件操作【详解】_第27张图片

六、文件读取结束的判定 

  •  fgetc  判断是否为EOF
  •  fgets 判断返回值是否为NULL
  •  fread 判断返回值是否小于实际要读的个数

在这里 要多说一个 feof

feof: 用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束 

feof 用来判断是什么原因结束的

七、文件缓冲区 

文件缓冲区是指在操作系统中,将文件读取或写入时,先将数据缓存到内存中的一段空间,等待一定量的数据积累后再进行实际的读写操作。这样做的好处是可以提高文件输入和输出效率,减少文件系统的负担。而且通过缓冲,可以减少磁盘或网络的读写次数,降低系统负担,提高程序的性能。

文件缓冲区包括输入缓冲区和输出缓冲区。输入缓冲区用于存储等待处理的输入数据,而输出缓冲区用于存储等待写入文件的数据。当输入缓冲区或输出缓冲区已满或达到一定数量时,系统才会进行实际的读写操作。当文件操作完成时,缓冲区的内容会被写入或者读取出来,或者在程序运行结束时被自动释放。

在编程中,我们可以通过控制文件缓冲区大小和刷新缓冲区来提高程序效率。如果希望立即将缓冲区的数据写入磁盘或读取最新数据,可以使用flush()、fflush()等函数来强制刷新缓冲区。

#include
#include
int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//
	fprintf(pf,"hello\n");
	fflush(pf);  //刷新后就会出现在文件中
	Sleep(10000);//睡眠10秒
	fprintf(pf,"world");
	fclose(pf);
	pf = NULL;
	return 0;
}

你可能感兴趣的:(【C语言】,c语言)