C文件操作

发布于:2024-06-12 ⋅ 阅读:(52) ⋅ 点赞:(0)

目录

1. 为什么要使用文件

2. 什么是文件

2.1 程序文件

2.2 数据文件

2.3 文件名

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

​编辑4.文件的打开和关闭

4.1 流和标准流

4.1.1 流

4.1.2 标准流

4.2 文件指针

4.3 文件的打开和关闭

5. 文件的顺序读写

5.1 顺序读写函数介绍

5.1.1 fputc函数:

5.1.2 fgetc函数:

​编辑5.1.3 fputs函数:

5.1.4 fgets函数:

 5.1.5 fprintf函数:

5.1.6 fscanf函数:

5.1.7 fwrite函数:

5.1.8 fread函数

5.2 函数对比

5.2.1 sprintf函数:

5.2.2 sscanf函数:

6. 文件的随机读写

6.1 fseek函数

6.2 ftell函数

6.3 rewind函数

7. 文件读取结束的判定

7.1 被错误使用的 feof

8. 文件缓冲区 


1. 为什么要使用文件

如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据运行持久化的保存,我们可以使用文件。

例子:

当我第一次运行代码将10放到a里面,程序结束之后,我第二次运行代码结构发现a还是0, 原因就是a是内存上的一块区域,只要程序退出,数据就丢了,如果我们想把数据持久化的保存,我们可以使用文件,因为文件是存在硬盘上的。

2. 什么是文件

硬盘上的文件是文件。

但是在程序设计中,我们⼀般谈的⽂件有两种:程序⽂件,数据⽂件(从⽂件功能的⻆度来分类 的)。

2.1 程序文件

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

2.2 数据文件

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

本文章讨论的是数据文件。

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

2.3 文件名

⼀个⽂件要有⼀个唯⼀的文件标识,以便⽤⼾识别和引用。

⽂件名包含3部分:文件路径+文件名主干+文件后缀,一般我们说文件名就说test.txt,其实加上路径也是可以的。

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

根据数据的组织形式,数据⽂件被称为文本文件或者⼆进制文件。

数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的⽂件中,就是⼆进制文件。

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

⼀个数据在文件中是怎么存储的呢?

字符⼀律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使⽤二进制形式存储。 如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符⼀个字节),而二进制形式输出,则在磁盘上只占4个字节。

文本文件:

这种就是文本文件, 10000一共占5个字符,这种我们能看懂的被称为文本文件。

二进制文件:

虽然不能直接写出二进制文件,但我们可以用代码的方式实现一个二进制文件。

#include <stdio.h>
int main()
{
   int a = 10000;
   FILE* pf = fopen("test.txt", "wb");//以二进制写的方式打开文件
   fwrite(&a, 4, 1, pf);//⼆进制的形式写到⽂件中
   fclose(pf);//关闭文件
   pf = NULL;//置为空指针
   return 0;
}

将代码运行。 

代码运行结束,让我们看一下test.txt的内容。

这个文件我们直接看不懂,因为它是一个二进制文件,用文本文件解读的话根本不行。

那我们可以用vs来打开。

那这个是什么意思呢?画图来看一下。

4.文件的打开和关闭

4.1 流和标准流

4.1.1 流

我们程序的数据需要输出到各种外部(文件,光盘,软盘,硬盘,网络)设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出 操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念,我们可以把流 想象成流淌着字符的河。

C程序针对文件,画面,键盘等的数据输入输出操作都是通过流来操作的。

⼀般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作,然后关闭流。

4.1.2 标准流

那为什么我们从键盘输入数据,向屏幕上输出数据,并没有打开流呢?

那是因为C语⾔程序在启动的时候,默认打开了3个流:

1. stdin - 标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据。

2.stdout - 标准输出流,大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出 流中。

3. stderr - 标准错误流,大多数环境中输出到显示器界面。

这是默认打开了这三个流,我们使用scanf、printf等函数就可以直接进行输入输出操作的。 stdin、stdout、stderr 三个流的类型是: FILE * ,通常称为⽂件指针。

C语⾔中,就是通过 FILE* 的文件指针来维护流的各种操作的。

4.2 文件指针

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

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

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;

不同的C编译器的FILE类型包含的内容不完全相同,

但是大同小异,每当打开⼀个文件的时候,系统会根据文件的情况自动创建⼀个FILE结构的变量,并填充其中的信息,不必关心细节。

⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

以创建⼀个FILE*的指针变量: 

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

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

比如:

文件是在硬盘里面,而文件信息区是在内存里面。 

4.3 文件的打开和关闭

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

打开文件其实就是打开流,读写文件其实就是读写流,关闭文件其实就是关闭流。

在编写程序的时候,在打开⽂件的同时,都会返回⼀个FILE*的指针变量指向该文件,也相当于建立了 指针和文件的关系。

ANSI C规定使用fopen函数来打开文件, fclose 来关闭文件。

打开文件函数:

fopen - C++ Reference

关闭文件函数:

fclose - C++ Reference

文件使用方式 含义 如果该文件不存在
“r”(只读) 为了输⼊数据,打开⼀个已经存在的文本文件 出错
“w”(只写) 为了输出数据,打开⼀个文本文件 建立一个新的文件
“a”(追加) 向文本文件尾添加数据 建立一个新的文件
“rb”(只读) 为了输⼊数据,打开⼀个⼆进制文件 出错
“wb”(只写) 为了输⼊数据,打开⼀个⼆进制文件 建立一个新的文件
“ab”(追加) 向⼀个⼆进制文件尾添加数据 建立一个新的文件
“r+”(读写) 为了读和写,打开⼀个文本文件 出错
“w+”(读写) 为了读和写,建立⼀个新的文件 建立一个新的文件
“a+”(读写) 打开⼀个文件,在文件尾进行读写 建立一个新的文件
“rb+”(读写) 为了读和写打开⼀个⼆进制文件 出错
“wb+”(读 写) 为了读和写,新建⼀个新的⼆进制文件 建立一个新的文件
“ab+”(读 写) 打开⼀个⼆进制文件,在文件尾进行读和写 建立一个新的文件

注意:以"w"(只写)的形式打开文件,如果文件存在,会将存在的文件清空打开,不存在会创建新的文件。

打开文件,以只读的方式打开:

#include <stdio.h>
int main()
{
	//打开成功返回有效地址,打开失败,返回NULL
	FILE* pf = fopen("test.txt", "r");//打开文件
	if (pf == NULL)//判断是否为空指针
	{
		perror("fopen");
		return 1;
	}

	//读,写文件
	//.......

	fclose(pf);//关闭文件
	pf = NULL;//置为空指针
	return 0;
}

打开文件,以只写的方式打开:

#include <stdio.h>
int main()
{
	//打开成功返回有效地址,打开失败,返回NULL
	FILE* pf = fopen("test.txt", "w");//打开文件
	if (pf == NULL)//判断是否为空指针
	{
		perror("fopen");
		return 1;
	}

	//读,写文件
	//.......

	fclose(pf);//关闭文件
	pf = NULL;//置为空指针
	return 0;
}

前面我们看到,我们只能读或者写我们c语言工程下面的文件,那如果我有一个test.txt的文件在桌面的话,这个时候就不能只写文件名和后缀了,必须加上文件路径。

绝对路径:

 

这个时候我的文件在桌面上。

#include <stdio.h>
int main()
{
	FILE* pf = fopen("F:\\Documents\\桌面\\test.txt", "w");//绝对路径
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读,写文件
	//.......

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

 那么这个时候我们就得在代码中添加我们文件的路径了,为了防止转义字符,\前面必须加一个\。

当我们代码运行完成,里面的东西也被清空了。 

相对路径 - 相对于当前位置的路径:

我先我把我要操作的文件挪到工程外的的目录,并且文件里面有东西。 

#include <stdio.h>
int main()
{
	FILE* pf = fopen(".\\..\\test.txt", "w");//相对路径
	//.表示当前路径
	//..表示上一级路径
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读,写文件
	//.......

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

 当我运行代码,我的文件里的东西确实被清空了。 

注意:以读的形式打开,就只能读。

        以写的形式打开,就只能写。

5. 文件的顺序读写

5.1 顺序读写函数介绍

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

字符写入函数

fputc - C++ Reference

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");//写的方式,没有文件新建文件,有文件清空内容
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputc('q',pf);//写入字符q
	fputc('w',pf);
	fputc('e',pf);
	fputc('r',pf);
	fputc('t',pf);

	//关闭文件
	fclose(pf);
	pf = NULL;//置为空指针,否则会是野指针
	return 0;
}

代码运行,看看结果:

文件里面确实写入了数据,那这个到底是怎么写入的呢?

 可以使用循环的方式将16个字母全部写进去。

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

5.1.2 fgetc函数:

字符读取函数

fgetc - C++ Reference

EOF - end of file  文件结束标志。

当我们读文件遇到文件末尾的时候,这个时候会设置一个文件的结束标志,这个标志就是EOF。

 EOF的本质其实就是-1。

#include <stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int i = fgetc(pf);
	printf("%c\n", i);
	i = fgetc(pf);
	printf("%c\n", i);
	i = fgetc(pf);
	printf("%c\n", i);
	i = fgetc(pf);
	printf("%c\n", i);
	i = fgetc(pf);
	printf("%c\n", i);

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

运行代码:

 读取文件中的全部字符。

#include <stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c ", ch);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

 运行代码。

读多个字符,可以使用while循环。

 stdin - 输入流,读的形式打开文件,文件输入流。

stdout - 输出流,写的形式打开文件,文件输出流。

那么前面的表格我们可以看到fgetc和fputc是针对所以流的,那肯定也能针对stdin和stdout。

#include <stdio.h>
int main()
{
	int ch = fgetc(stdin);
	fputc(ch, stdout);
	return 0;
}
5.1.3 fputs函数:

写字符串函数

fputs - C++ Reference

#include <stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror(pf);
		return 1;
	}

	char* p = "hello world!\n";
	fputs(p, pf);
	fputs("are you ok?\n", pf);

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

运行代码:

 第二个字符串也换行了,所以光标在文本文件的第三行。

5.1.4 fgets函数:

读字符串函数

fgets - C++ Reference

#include <stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char arr[20] = "xxxxxxxxxxxxxxxx";
	fgets(arr, 5, pf);
	printf("%s\n", arr);
	fclose(pf);
	pf = NULL;
	return 0;
}

调试:

通过调试可以看到当我们想读取5个字符.参数写5的时候实际只读4个字符,它还会在后面放一个'\0'。

当我想读15个字符的时候这里却只读了不到15个,原因就是遇到换行也结束读取。 

#include <stdio.h>
int main()
{
	char arr[20] = { '\0' };
	fgets(arr, 20, stdin);
	fputs(arr, stdout);
	return 0;
}

这两个函数是针对所有流的,可以在屏幕上输入输出。

 5.1.5 fprintf函数:

fprintf - C++ Reference

fprintf函数和printf函数的对比:

#include <stdio.h>
struct Stu
{
	char name[20];
	int age;
	float score;
};

int main()
{
	struct Stu s = { "LiXiangSi",20,99.5 };
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fprintf(pf, "%s %d %.1f",s.name,s.age,s.score);
	fclose(pf);
	pf = NULL;
	return 0;
}

运行代码:

fprintf针对的是所有格式的数据。 

5.1.6 fscanf函数:

scanf函数和fscanf函数的对比: 

#include <stdio.h>
struct Stu
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct Stu s = { 0 };
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));
	printf("%s %d %.1f\n", s.name, s.age, s.score);
	fprintf(stdout,"%s %d %.1f\n", s.name, s.age, s.score);
	return 0;
}

运行代码:

5.1.7 fwrite函数:

fwrite - C++ Reference

#include <stdio.h>
struct Stu
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct Stu s = { "Aurora",20,88.8f };
	FILE* pf = fopen("test.txt", "wb");//二进制写的方式
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fwrite(&s, sizeof(struct Stu), 1, pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

运行代码:

二进制文件,我们是看不懂的。

5.1.8 fread函数

fread - C++ Reference

#include <stdio.h>
struct Stu
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct Stu s = { 0 };
	FILE* pf = fopen("test.txt", "rb");//二进制写的方式
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fread(&s, sizeof(struct Stu), 1, pf);
	printf("%s %d %f\n", s.name, s.age, s.score);
	fclose(pf);
	pf = NULL;
	return 0;
}

 这个函数其实返回的是实际读到的字符个数,比如9给字符,第一次读3个,第2次读3个,那么第3次就只能读两个了。

5.2 函数对比

scanf/fscanf/sscanf函数的对比。

printf/fprintf/sprintf函数的对比。

5.2.1 sprintf函数:

sprintf - C++ Reference

@include <stdio.h>
struct Stu
{
	char name[20];
	int age;
	float score;
};
int main()
{
	char ch[100] = { '\0' };
	struct Stu s = { "Aurora",20,96.5f };
	sprintf(ch, "%s %d %f", s.name, s.age, s.score);
	printf("%s\n", ch);
	return 0;
}

运行结果:

sprintf函数不管什么类型的数据都可以转换成字符串。

5.2.2 sscanf函数:

sscanf - C++ Reference

#include <stdio.h>

struct Stu
{
	char name[20];
	int age;
	float score;
};
int main()
{
	char ch[100] = { '\0' };
	struct Stu tem = { 0 };
	struct Stu s = { "Aurora",20,96.5f };
	//将格式化数据提取成字符串
	sprintf(ch, "%s %d %f", s.name, s.age, s.score);
	//从字符串中提取格式化的数据,什么样的顺序放进去的就需要什么样的顺序来提
	sscanf(ch, "%s %d %f", tem.name, &(tem.age), &(tem.score));
	printf("%s %d %f\n", tem.name, tem.age, tem.score);
	return 0;
}

6. 文件的随机读写

刚刚的文件顺序读写就是文件一行一行的读写,那么文件随机读写就是想在哪读就在哪读,想在哪写就在哪写。

6.1 fseek函数

根据文件指针的位置和偏移量来定位文件指针(文件内容的光标)。

fseek - C++ Reference

 

 

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

	fclose(pf);
	return 0;
}

运行代码: 

这样的代码,第一次读到a,光标往后移,第二次就读到b了,光标往后移,第三次就读到c了,光标继续往后移,那么下一次肯定读到的是d,但是我现在不想读d了,想读e,那就得自己定位文件指针的位置。

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

	//fseek(pf, 4, SEEK_SET);//起始位置,偏移4
	//fseek(pf, 1, SEEK_CUR);//当前位置,偏移1
	fseek(pf, -2, SEEK_END);//文件末尾,偏移-2

	ch = fgetc(pf);//e
	printf("%c\n", ch);
	fclose(pf);
	return 0;
}

运行代码: 

 通过代码的运行结果可以看出,第四次调用fgetc函数获取的本来是字符d,但是在第四次调用之前使用fseek函数重新定位了文件指针。

6.2 ftell函数

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

ftell - C++ Reference

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

	fseek(pf, -2, SEEK_END);

	ch = fgetc(pf);//e
	printf("%c\n", ch);
	
	printf("%d\n", ftell(pf));//文件指针相较于起始地址的偏移量

	fclose(pf);
	return 0;
}

运行代码: 

偏移量是5,第一次调用fgetc函数,得到a,第二次调用得到b,第三次调用得到c,然后使用fseek函数将文件指针指向文件末尾的-2的位置,也就是e的位置,然后又调用fget函数,文件指针又指向f,f到文件指针起始位置的偏移量就是5。

6.3 rewind函数

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

rewind - C++ Reference

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

	fseek(pf, -2, SEEK_END);

	ch = fgetc(pf);//e
	printf("%c\n", ch);
	
	printf("%d\n", ftell(pf));

	rewind(pf);//文件指针回到起始位置
	ch = fgetc(pf);
	printf("%c\n", ch);

	fclose(pf);
	return 0;
}

运行代码:

我们使用ftell函数计算的偏移量是5,就说明文件指针指向的是f,然后我们调用rewind函数让文件指针回到起始位置,然后又调用fgetc函数取字符,取到的是a,就说明确实回到文件的起始位置了。

7. 文件读取结束的判定

7.1 被错误使用的 feof

在文件读取过程中,不能用feof函数的返回值直接来判断文件的是否结束。

feof 的作用是:当⽂件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。

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

例如:

fgetc 判断是否为 EOF。

fgets 判断返回值是否为 NULL。

2. ⼆进制文件的读取结束判断,判断返回值是否小于实际要读的个数。

例如:

fread判断返回值是否小于实际要读的个数。

文本文件:

#include <stdio.h>
int main()
{
	int ch = 0;//不能是char类型,以为要判断EOF(-1)
	FILE* pf = fopen("test.txt", "r");
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	while ((ch = fgetc(pf)) != EOF)//读取字符
	{
		putchar(ch);
	}

	if (ferror(pf))//判断是否错误
		printf("读取时出现I/O错误\n");
	else if (feof(pf))//判断是否遇到文件结束标志
		printf("遇到文件结束标志\n");
	return 0;
}

二进制文件:

#include <stdio.h>
enum{SIZE = 5};
int main()
{
	double arr1[SIZE] = { 1.,2.,3.,4.,5., };
	FILE* pf = fopen("test.txt", "wb");
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	//写
	fwrite(arr1, sizeof * arr1, SIZE, pf);
	fclose(pf);
	//读
	pf = fopen("test.txt", "rb");
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	double arr2[SIZE] = { '\0'};
	size_t value = fread(arr2, sizeof * arr2, SIZE, pf);
	if (value == SIZE)
	{
		printf("读取成功,数组内容是:\n");
		for (int i = 0; i < SIZE; i++)
			printf("%f ", arr2[i]);
	}
	else
	{
		if (ferror(pf))
			printf("读取错误\n");
		else if (feof(pf))
			printf("遇到文件末尾\n");
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

将test1的内容拷贝到test2中。

#include <stdio.h>
int main()
{
	FILE* Open_File_Read = fopen("test1.txt", "r");
	if (!Open_File_Read)
	{
		perror("fopen file1");
		return 1;
	}
	FILE* Open_File_Write = fopen("test2.txt", "w");
	if(!Open_File_Write)
	{
		fclose(Open_File_Read);
		Open_File_Read = NULL;
		perror("fopen file2");
		return 1;
	}

	int ch = 0;
	while ((ch = fgetc(Open_File_Read)) != EOF)
	{
		fputc(ch, Open_File_Write);
	}

	fclose(Open_File_Read);
	Open_File_Read = NULL;
	fclose(Open_File_Write);
	Open_File_Write = NULL;
	return 0;
}

ferror和feof的返回值

 

8. 文件缓冲区 

ANSIC 标准采用“缓冲系统” 处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每⼀个正在使用的文件开辟⼀块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓 冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘⽂件中读取数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的

测试代码: 

#include <stdio.h>
#include <windows.h>
//vs2019
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);//睡眠10秒是看fflush起作用还是fclose起作用
	fclose(pf);
	//注:fclose在关闭⽂件的时候,也会刷新缓冲区
	pf = NULL;
	return 0;
}

运行此代码,在刷新缓冲区之前文件是没有内容的,刷新缓冲区之后文件才有内容。

如果fflush不能使用也可以使用fclose来刷新缓冲区。

因为有缓冲区的存在,C语⾔在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。

如果不做,会导致文件读写有问题。


网站公告

今日签到

点亮在社区的每一天
去签到