第十二章 初窥天机之文件操作

当前计算机存在的文件形式多种多样,比如txt,dat,word,xml,json等各式各样的文件形式。这些文件格式都是常见的,并且在将来的编程中经常使用的文件格式。今后我们主要讲解dat和txt文件格式的读写。当然我们现在讲解的都是最简单的文件操作。如果对于程序员见得最多的文件格式恐怕是“.c”文件,“.cpp”文件,“.exe”文件了,下面让我们详细了解一下什么是文件吧!

 

12.1 文件

什么是文件?文件在程序设计中表示存储在外部介质上数据的集合。外部介质有:硬盘,U盘,光盘等。数据是以文件的形式存放在外部介质上的。而操作系统也是以文件的形式存储的。

我们之前也说了,文件有多种类型,而程序设计中常用的就是程序文件和数据文件。程序文件就是程序源文件或者目标文件或者可执行文件。数据文件就是程序操作中需要读取或写入的文件形式或者数据。这一章我们主要讲的就是如何通过程序操作数据文件。

说到操作数据文件,就要考虑两个问题,那就是如何读取数据文件?如何写入数据文件?这也就不可避免的谈到数据流的操作。

我们这章讲的文件操作,主要针对文本文件和二进制文件,操作文件就需要有文件名。每个文件都有一个文件名,这样方便用户识别和引用。一个文件有唯一的标识,文件标识包括3部分:(1)文件路径;(2)文件名主干;(3)文件后缀。

文件路径表示文件在外部存储设备中的位置。比如:

D:\temp\data\data.txt

其中,D表示盘符,\temp\data\表示路径,data.txt表示文件名。这个是绝对路径,我们还可以写成相对路径,比如:

..\data\data.txt

或者

.\data\data.txt

第一个相对路径表示从当前文件夹开始的上一级目录(两个“..”表示上一级目录)下有文件夹data,data文件夹下有文件data.txt。第二个相对路径表示从当前目录(一个“.”表示当前目录)开始,当前目录下有一个文件夹data,在文件夹data下有一个文本文件data.txt。

我们在写程序的时候常用相对路径,因为绝对路径下的文件或者文件夹,在别人的电脑下就不是那个路径了,比如我们电脑下的路径是“D:\temp\data\data.txt”,但是在另一个人的电脑下路径可能就是“C:\temp\data\data.txt”,所以写成绝对路径是不合适的。我们可以把资源放到工程中,并把路径写成相对路径就行了。

说到文件的读取与写入,就不得不考虑缓存区的问题了,我们大学都学过在硬盘和CPU之间都添加了一些硬件设备,比如内存,缓存等,他们的目的就是解决CPU的高速度与硬盘的低速之间速度不匹配的问题,那么这就是缓存区使用的目的。

文件操作就会有文件缓存区,文件系统会自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓存区。从内存向磁盘输出数据必须先送到内存中的缓冲区,当装满缓冲区后才一起送到磁盘中。同样,如果从磁盘向计算机读入数据,则一次从磁盘中将一批数据读入到内存缓冲区,然后在从缓存区逐个将数据读入程序的数据区。

 

 

12.2 文件操作

针对文件的操作很多,比如打开文件,关闭文件,读文件,写文件,文件定位,文件出错检查等。我们经常会听到这样一句话:把大象关进冰箱需要几步?当然大家现在都知道了,首先打开冰箱,然后把大象放进冰箱,最后关上冰箱。大象放进冰箱的步骤就和我们的文件操作一样,我们操作文件也是首先打开文件,然后操作文件,最后关闭文件。这里的操作文件就是读文件或者写文件或者定位文件位置等。我们有没有想过打开冰箱或者放进大象或者关闭冰箱都需要什么?这个简单,当然需要手了。可是你有没有想过打开文件,操作文件,关闭文件呢?那么这个就需要文件指针了。

 

12.2.1 打开文件

打开文件我们使用标准输入输出函数fopen,该函数由ANSI C规定。函数原型如下:

所需头文件

#include <stdio.h>

函数原型

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

函数传入值

path:要操作的路径名与文件名

mode:打开模式

函数功能

打开指定的文件

函数返回值

文件打开成功,返回指向文件流的指针。文件打开失败,返回NULL。并把错误代码存在errno中。

其中,我们需要介绍一下mode这个参数,因为不同的参数代表打开方式的不同。如下:

“r”以只读的方式打开文件,该文件必须存在。

“r+”以可读写的方式打开文件,该文件必须存在。

“rb+”以读写的方式打开一个二进制文件,该文件必须存在。

“w”以只写的方式打开文件,若文件存在,则文件长度清0,即文件中原内容消失。若文件不存在,则建立该文件。

“w+”以可读写的方式打开文件,若文件存在,则文件长度清0,即文件中原内容消失。若文件不存在,则建立该文件。

“a”以附加的方式打开可读写文件。若文件存在,写入的数据会被添加到文件末尾,即原文件中内容被保留。若文件不存在,则建立该文件。

“a+”以附件的方式打开可读写文件。若文件存在,,写入的数据会被添加到文件末尾,即原文件中内容被保留。若文件不存在,则建立该文件。

如果对应的模式下添加“b”或者“t”表示对相应的二进制或者文本文件操作,比如“rb+”等。如果添加“x”表示为独占模式,如果文件已经存在或者无法创建都会导致打开文件失败。

打开方式

说明

r

以只读方式打开文件,该文件必须存在。

r+

以读/写方式打开文件,该文件必须存在。

rb+

以读/写方式打开一个二进制文件,只允许读/写数据。

rt+

以读/写方式打开一个文本文件,允许读和写。

w

打开只写文件,若文件存在则长度清为0,即该文件内容消失,若不存在则创建该文件。

w+

打开可读/写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。

a

以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留(EOF符保留)。

a+

以附加方式打开可读/写的文件。若文件不存在,则会建立该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(原来的EOF符 不保留)。

wb

以只写方式打开或新建一个二进制文件,只允许写数据。

wb+

以读/写方式打开或建立一个二进制文件,允许读和写。

wt+

以读/写方式打开或建立一个文本文件,允许读写。

at+

以读/写方式打开一个文本文件,允许读或在文本末追加数据。

ab+

以读/写方式打开一个二进制文件,允许读或在文件末追加数据。

二进制和文本模式的区别:

  • 在windows系统中,文本模式下,文件以"\r\n"代表换行。若以文本模式打开文件,并用fputs等函数写入换行符"\n"时,函数会自动在"\n"前面加上"\r"。即实际写入文件的是"\r\n" 。
  • 在类Unix/Linux系统中文本模式下,文件以"\n"代表换行。所以Linux系统中在文本模式和二进制模式下并无区别。

 

 

讲了这么多的理论,感觉是不是看起来都好枯燥,那么让我们简单的看一个简单的例子吧。

【例12.1】fopen函数的认识。

解题思路:我们此处仅仅简单的使用fopen函数打开一个文件,查看文件的运行结果,了解fopen函数的基本使用。

编写程序:

#include <stdio.h>
int main()
{
	FILE* fp = NULL;//定义文件指针
	fp = fopen("D:\\1.txt", "r");//打开D盘下的1.txt文件,以只读的方式
	if (NULL == fp)//如果文件不存在,则打开失败
	{
		fprintf(stderr, "文件打开失败!\n");//输出打开失败信息
		return -1;
	}
	fprintf(stderr, "文件打开成功!\n");//输出打开成功信息
	fclose(fp);//我们还没有讲到该函数,但是它一定要和fopen函数成对出现。否则会出现内存泄漏
	fp = NULL;//此处是安全的处理文件指针,使其指向空
	return 0;
}

1. 运行结果(在D盘下没有1.txt文件时):

文件打开失败!

Press any key to continue

 

1. 运行结果(在D盘下存在1.txt文件时):

文件打开成功!

Press any key to continue

 

程序分析:使用fopen函数打开一个文件,如果打开成功,返回要打开文件的文件指针。如果打开失败,返回NULL。我们知道“r”模式是以只读的方式打开文件,该文件必须存在。我们第一次的运行结果是在D盘中没有建立1.txt的条件下,所以该程序的运行结果是:文件打开失败!。第二次的运行结果是在D盘下建立1.txt的条件下,所以运行结果为:文件打开成功!。

 

注意:此处我们使用的模式仅仅是“r”,大家可以尝试使用其他模式。我们也会在后面的程序中陆续讲到其他模式的使用,并且有相应代码

 

上面的这个程序我们使用的是“r”模式,我们知道该模式是以只读的方式打开文件,该文件必须存在。既然是只读,那么你就不能通过文件指针往文件中写入数据,只能从文件中读取数据。如果以“w”模式来写一个程序,让大家理解一下只写的方式。

首先,“w”模式是以只写的方式打开文件,若文件存在,则文件长度清0,即文件中原内容消失。若文件不存在,则建立该文件。

【例12.2】fopen以“w”模式打开文件。

解题思路:fopen函数以“w”模式打开文件。程序代码和例12.1类似,就修改了打开模式处。

编写程序:

#include <stdio.h>
int main()
{
	FILE* fp = NULL;//定义文件指针
	fp = fopen("D:\\1.txt", "w");//打开D盘下的1.txt文件,以只写的方式
	if (NULL == fp)//如果文件不存在,则打开失败
	{
		fprintf(stderr, "文件打开失败!\n");//输出打开失败信息
		return -1;
	}
	fprintf(stderr, "文件打开成功!\n");//输出打开成功信息
	fclose(fp);//我们还没有讲到该函数,但是它一定要和fopen函数成对出现。否则会出现内存泄漏
	fp = NULL;//此处是安全的处理文件指针,使其指向空
	return 0;
}

1. 运行结果(在D盘下存在1.txt文件时):

文件打开成功!

Press any key to continue

 

2. 运行结果(在D盘下没有1.txt文件时):

文件打开成功!

Press any key to continue

 

程序分析:观察两个运行结果发现结果都是:文件打开成功!。首先我们知道“w”是以只写的方式打开文件,若文件存在,则文件长度清0,即文件中原内容消失。若文件不存在,则建立该文件。就是说第一次运行结果是在D盘下存在1.txt的条件下,文件存在,打开一定会成功。如果在D盘目录中存在1.txt的条件下,并且1.txt有内容,那么运行程序,会发现程序中的数据清空,这就是w的功能(文件中有数据,则删除)。第二次运行结果是在D盘下,我们手动删除文件后,再运行程序,会发现程序结果是打开文件成功,这是因为如果文件不存在,程序会自动的创建一个1.txt的文件。那么有人就疑惑了,怎么才能使文件打开失败呢?如果按照之前说的,这个就无法出现文件打开失败的情况了。事实上,现如今出现NULL的情况很少了,那是因为我们的内存和硬盘空间都已经很大了,如果两者都很小,小的不足以创建文件,那么就会出现文件打开失败。

 

 

12.2.2 关闭文件

上一节我们讲了打开文件使用标准输入输出函数fopen,这一节我们讲解如何关闭fopen函数。

fclose是一个函数名,功能是关闭一个流。fclose函数可以把缓冲区中最后的数据输出到内核缓冲区,并且

函数原型如下:

所需头文件

#include <stdio.h>

函数原型

int fclose(FILE* stream);

函数传入值

文件指针

函数功能

关闭指定的文件

函数返回值

如果流成功关闭,fclose返回0,否则返回EOF(-1)。

上一节中我们已经应用了文件的关闭操作,其实应用就是那么简单,现在打算再给大家举一个例子,目的是为了能够进一步学习文件的相关操作。

【例12.3】读数据从文件。

解题思路:文件的关闭很简单,就一句代码即可,为了多学一点,我此处会再写一个读文件操作,使用大家更多的理解文件操作。我们需提前往D盘的1.txt文件中写入“hello world!”,这样在读数据的时候,文件中就会有数据可读,可观察到效果。

编写程序:

#include <stdio.h>
int main()
{
	char cGetCh=0;
	FILE* fp = NULL;//定义文件指针
	fp = fopen("D:\\1.txt", "r");//打开D盘下的1.txt文件,以只读的方式
	if (NULL == fp)//如果文件不存在,则打开失败
	{
		fprintf(stderr, "文件打开失败!\n");//输出打开失败信息
		return -1;
	}
	fprintf(stderr, "文件打开成功!\n");//输出打开成功信息
	//fgetc函数每次读从文件读取一个字符的数据,直到文件结束EOF为止。读到的字符放cGetCh中
	while ((cGetCh = fgetc(fp)) != EOF)
	{
		printf("%c", cGetCh);//输出从文件中获取的字符数据
	}
	putchar('\n');//输出换行符
	fclose(fp);//我们还没有讲到该函数,但是它一定要和fopen函数成对出现。否则会出现内存泄漏
	fp = NULL;//此处是安全的处理文件指针,使其指向空
	return 0;
}

运行结果:

文件打开成功!

hello world!

Press any key to continue

 

程序分析:这是一个读文件数据并显示结果的程序。该程序是从文件1.txt中读取数据并在显示器上显示结果。这个程序中我们用到一个函数叫fgetc函数,这个函数具有从文件中读取一个字符并把结果返回的功能。我们把返回的结果cGetCh输出到屏幕上去。直到读到文件末尾,即当读出的数据为EOF(-1)时,结束读取。这个函数我们还会在后面的章节介绍。

 

既然讲了读文件数据,我们也举一个写文件的例子吧!对应的函数是fputc函数,详细的介绍我们会在后面中讲解,此处仅讲应用。

【例12.4】写数据到文件。

解题思路:我们使用fputc函数,往文件中写入数据,该函数有两个参数,第一个是文件指针,就是fopen的返回值,第二个参数就是要写入文件中的字符。操作流程就是:先使用fopen函数打开文件,然后fputc函数把数据写入文件,最后fclose函数关闭文件。

编写程序:

#include <stdio.h>
int main()
{
	char cGetCh[]="hello world!!!";
	int i = 0;
	FILE* fp = NULL;//定义文件指针
	fp = fopen("D:\\1.txt", "w");//打开D盘下的1.txt文件,以只写的方式
	if (NULL == fp)//如果文件不存在,则打开失败
	{
		fprintf(stderr, "文件打开失败!\n");//输出打开失败信息
		return -1;
	}
	fprintf(stderr, "文件打开成功!\n");//输出打开成功信息
	//fputc函数每次写一个字符到文件中。
	while (cGetCh[i] != '\0')
	{
		fputc(cGetCh[i], fp);
		printf("%c", cGetCh[i]);//输出从文件中获取的字符数据
		i++;
	}
	putchar('\n');//输出换行符
	fclose(fp);//我们还没有讲到该函数,但是它一定要和fopen函数成对出现。否则会出现内存泄漏
	fp = NULL;//此处是安全的处理文件指针,使其指向空
	return 0;
}

运行结果:

文件打开成功!

hello world!!!

Press any key to continue

 

程序分析:打开文件D:\\1.txt,文件中有“hello world!!!”,说明我们写入文件操作完成。其实文件操作就是这么简单。fputc函数我们会在后面的章节中讲解。

 

12.2.3 写文件

12.2.3.1 fprintf函数

fprintf函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

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

函数传入值

stream:文件指针,指向将要写入的文件流。

format:输出格式。

[argument]:附加参数列表。

函数功能

传送格式化输出到一个文件中与打印机输出。

函数返回值

1. 如果成功,则返回实际读取到的字符数。

2. 如果失败,则返回负数。

【例12.5】使用fprintf函数存储羊驼图像

解题思路:我们写一个简单的程序,来讲解fprintf函数的使用,使大家看到代码就知道怎么使用,让我们往下看吧。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	FILE *stream = fopen( "alpacas.txt", "w+" );
	if( !stream ){
		printf( "The file alpacas.txt was not opened\n" );
		return -1;
	}
	
	fprintf(stream, "%s", "┏┛┻━━━┛┻┓\n");
	fprintf(stream, "%s", "┃|||||||┃\n");
	fprintf(stream, "%s", "┃   ━   ┃\n");
	fprintf(stream, "%s", "┃ ┳┛ ┗┳ ┃围观是一种态度\n");
	fprintf(stream, "%s", "┃       ┃\n");
	fprintf(stream, "%s", "┃   ┻   ┃\n");
	fprintf(stream, "%s", "┃       ┃围观是为了提高知名度\n");
	fprintf(stream, "%s", "┗━┓   ┏━┛\n");
	fprintf(stream, "%s", "  ┃   ┃  \n");
	fprintf(stream, "%s", "  ┃   ┃  \n");
	fprintf(stream, "%s", "  ┃   ┃  \n");
	fprintf(stream, "%s", "  ┃   ┃  \n");
	fprintf(stream, "%s", "  ┃   ┗━━━┓\n");
	fprintf(stream, "%s", "  ┃             ┣┓\n");
	fprintf(stream, "%s", "  ┃围观专用宠物 ┃\n");
	fprintf(stream, "%s", "  ┗┓┓┏━┳┓┏┛\n");
	fprintf(stream, "%s", "   ┃┫┫ ┃┫┫\n");
	fprintf(stream, "%s", "   ┗┻┛ ┗┻┛\n");
	
	fclose( stream );
	return 0;
}

运行结果:

程序分析:程序非常简单,就是简单的使用fprintf函数把羊驼图像保存到文件。运行结果显示的就是存储到文件中的结果截图。

为了进一步讲解fprintf函数的使用,下面我们将用一个比较贴近实际应用的例子来讲解fprintf函数的使用。

【例12.6】使用fprintf函数存储学生成绩至文档

解题思路:我们使用fprintf实现学生成绩存储至文件中,使用fscanf从文件中读取学生成绩,并显示到界面上。

编写程序:

#include <stdio.h>
#include <stdlib.h>

typedef struct STUDENT{
	char id[9];
	char name[32];
	char course[32];
	float score;
}STUDENT;

int main( void )
{
	size_t i = 0;
	STUDENT student[2], tmp;
	memset(&student[0], 0, sizeof(STUDENT));
	strcpy(student[0].id, "20250001");
	strcpy(student[0].name, "zhangsan");
	strcpy(student[0].course, "Chinese");
	student[0].score = 92.0;

	memset(&student[1], 0, sizeof(STUDENT));
	strcpy(student[1].id, "20250002");
	strcpy(student[1].name, "lisi");
	strcpy(student[1].course, "English");
	student[1].score = 97.5;

	FILE *stream = fopen( "student.out", "w+" );
	if( !stream ){
		printf( "The file student.out was not opened\n" );
		return -1;
	}
	
	for( i = 0; i < 2; ++i ){
		fprintf( stream, "%s\t%s\t%s\t%.1f\n", student[i].id, student[i].name, student[i].course, student[i].score);
		printf("%10s, %10s, %10s, %3.1f\n", student[i].id, student[i].name, student[i].course, student[i].score);
	}
	
	fclose( stream );
	return 0;
}

运行结果:

  20250001,   zhangsan,    Chinese, 92.0

  20250002,       lisi,    English, 97.5

 

文件student.out中的内容如下:

程序分析:程序4~9行建立学生信息的结构体,在main函数中使用memset函数把结构体数组分别进行初始化并赋值。然后以“w+”的方式创建一个名为student.out的文本文件,如果student.out文件存在,则清空文件中内容再新内容存至该文档中。接着使用fprintf函数把结构体数组中的数据存至该文档中,并把存储内容显示在显示器上。最后记得使用fclose函数把文件指针关闭。另外,如果你不想创建以“.out”结尾的文件,也可以创建其他格式的文件,比如“.txt”结尾,都可以。

 

12.2.3.2 fputs函数

fputs函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

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

函数传入值

string:读取的待输出字符串。

stream:文件指针,指向将要写入的文件流。

函数功能

向指定的文件写入字符串。

函数返回值

1. 如果成功,则返回非负值。

2. 如果失败,则返回EOF。

fputs函数每次输入文件中的是一个字符串,当然使用fputs函数也可以把一个字符写到文件中,但是一个字符存写到文件中有专门的函数就是后面要讲的fputc函数。

为了对比讲解fputs函数与fprintf函数的异同点,以及对比后续写文件操作函数之间的异同点,我们将会使用不同的函数分别对羊驼图像和学生成绩执行写文件操作,提高大家对不同写文件函数的认识。

【例12.7】使用fputs函数存储羊驼图像

解题思路:例12.5中使用的是fprintf函数把羊驼图像存储到文件中,此处我们使用fputs函数,把羊驼图像存储到文件中,学术名称为“写文件”。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	FILE *stream = fopen( "alpacas.txt", "w+" );
	if( !stream ){
		printf( "The file alpacas.txt was not opened\n" );
		return -1;
	}
	
	fputs("┏┛┻━━━┛┻┓\n", stream);
	fputs("┃|||||||┃\n", stream);
	fputs("┃   ━   ┃\n", stream);
	fputs("┃ ┳┛ ┗┳ ┃围观是一种态度\n", stream);
	fputs("┃       ┃\n", stream);
	fputs("┃   ┻   ┃\n", stream);
	fputs("┃       ┃围观是为了提高知名度\n", stream);
	fputs("┗━┓   ┏━┛\n", stream);
	fputs("  ┃   ┃  \n", stream);
	fputs("  ┃   ┃  \n", stream);
	fputs("  ┃   ┃  \n", stream);
	fputs("  ┃   ┃  \n", stream);
	fputs("  ┃   ┗━━━┓\n", stream);
	fputs("  ┃             ┣┓\n", stream);
	fputs("  ┃围观专用宠物 ┃\n", stream);
	fputs("  ┗┓┓┏━┳┓┏┛\n", stream);
	fputs("   ┃┫┫ ┃┫┫\n", stream);
	fputs("   ┗┻┛ ┗┻┛\n", stream);
	
	fclose( stream );
	return 0;
}

运行结果:

程序分析:大家可以对比fputs函数和fprintf函数的异同。相同点是他们都能够实现把羊驼图像写入文件中,不同点是写文件时操作方式不同,fprintf函数支持可变参数,而fputs函数不支持可变参数。

 

【例12.8】使用fputs函数存储学生成绩至文档

解题思路:同样把fprintf函数改成fputs函数存储学生成绩到文件中。

编写程序

#include <stdio.h>
#include <stdlib.h>

typedef struct STUDENT{
	char id[9];
	char name[32];
	char course[32];
	float score;
}STUDENT;

int main( void )
{
	size_t i = 0;
	STUDENT student[2];
	char tmp[1024] = "\0";
	memset(&student[0], 0, sizeof(STUDENT));
	strcpy(student[0].id, "20250001");
	strcpy(student[0].name, "zhangsan");
	strcpy(student[0].course, "Chinese");
	student[0].score = 92.0;

	memset(&student[1], 0, sizeof(STUDENT));
	strcpy(student[1].id, "20250002");
	strcpy(student[1].name, "lisi");
	strcpy(student[1].course, "English");
	student[1].score = 97.5;

	FILE *stream = fopen( "student.txt", "w+" );
	if( !stream ){
		printf( "The file student.txt was not opened\n" );
		return -1;
	}
	
	for( i = 0; i < 2; ++i ){
		memset(tmp, 0, sizeof(tmp));
		sprintf(tmp, "%s\t%s\t%s\t%.1f\n", student[i].id, student[i].name, student[i].course, student[i].score);
		fputs( tmp, stream );
		printf("%s", tmp);
	}
	
	fclose( stream );
	return 0;
}

运行结果:

20250001        zhangsan        Chinese 92.0

20250002        lisi    English 97.5

 

文件student.out中的内容如下:

程序分析:因为fputs函数不想fprintf函数那样支持可变参数,所以在程序26行,需要先把待存储的字符串放入字符串tmp变量中,然后通过该变量写入文件中。

【例12.9】单独的fputs函数的使用

 

 

12.2.3.3 fputc函数

fputc函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

int fputc( int c, FILE *stream );

函数传入值

c:读取的待输出字符。

stream:文件指针,指向将要写入的文件流。

函数功能

将字符写入到文件中。

函数返回值

1. 如果成功,则返回写字符的ASCII码值。

2. 如果失败,则返回EOF(-1)。

注:每写完一个字符或一个字节的数据后,文件指针就会自动后移一个字节的位置。

【例12.10】使用fputc函数存储羊驼图像

解题思路:因为fputc函数每次只能写一个字符到文件中去,而羊驼有很多字符组成,所以我们需要把整个羊驼图像存放到一个字符数组中,然后通过字符数组一个一个写入文件中。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	char alpacas[1024];
	size_t i=0, len=0;
	FILE *stream = fopen( "alpacas.txt", "w+" );
	if( !stream ){
		printf( "The file alpacas.txt was not opened\n" );
		return -1;
	}
	
	strcat(alpacas, "┏┛┻━━━┛┻┓\n");
	strcat(alpacas, "┃|||||||┃\n");
	strcat(alpacas, "┃   ━   ┃\n");
	strcat(alpacas, "┃ ┳┛ ┗┳ ┃围观是一种态度\n");
	strcat(alpacas, "┃       ┃\n");
	strcat(alpacas, "┃   ┻   ┃\n");
	strcat(alpacas, "┃       ┃围观是为了提高知名度\n");
	strcat(alpacas, "┗━┓   ┏━┛\n");
	strcat(alpacas, "  ┃   ┃  \n");
	strcat(alpacas, "  ┃   ┃  \n");
	strcat(alpacas, "  ┃   ┃  \n");
	strcat(alpacas, "  ┃   ┃  \n");
	strcat(alpacas, "  ┃   ┗━━━┓\n");
	strcat(alpacas, "  ┃             ┣┓\n");
	strcat(alpacas, "  ┃围观专用宠物 ┃\n");
	strcat(alpacas, "  ┗┓┓┏━┳┓┏┛\n");
	strcat(alpacas, "   ┃┫┫ ┃┫┫\n");
	strcat(alpacas, "   ┗┻┛ ┗┻┛\n");
	
	len = strlen(alpacas);
	for ( i=0 ; i<len ; i++){
		fputc(alpacas[i], stream);
	}
	
	fclose( stream );
	return 0;
}

运行结果:

程序分析:执行完上面程序就会在文件alpacas.txt中生成羊驼图像。因为fputc函数每次只能写一个字符到文件中,所以我们首先定义一个较大数组,保证能够存放羊驼图像。然后使用strcat函数把所有羊驼图像存放到字符串变量alpacas中。接着使用fputc函数把alpacas中所有的值写入文件中。最后关闭文件指针。

 

【例12.11】使用fputc函数存储学生成绩至文档

解题思路:使用方法

编写程序:

#include <stdio.h>
#include <stdlib.h>

typedef struct STUDENT{
	char id[9];
	char name[32];
	char course[32];
	float score;
}STUDENT;

int main( void )
{
	size_t i = 0, j = 0, len = 0;
	STUDENT student[2];
	char tmp[1024] = "\0";
	memset(&student[0], 0, sizeof(STUDENT));
	strcpy(student[0].id, "20250001");
	strcpy(student[0].name, "zhangsan");
	strcpy(student[0].course, "Chinese");
	student[0].score = 92.0;

	memset(&student[1], 0, sizeof(STUDENT));
	strcpy(student[1].id, "20250002");
	strcpy(student[1].name, "lisi");
	strcpy(student[1].course, "English");
	student[1].score = 97.5;

	FILE *stream = fopen( "student.txt", "w+" );
	if( !stream ){
		printf( "The file student.txt was not opened\n" );
		return -1;
	}
	
	for( i = 0; i < 2; ++i ){
		memset(tmp, 0, sizeof(tmp));
		sprintf(tmp, "%s\t%s\t%s\t%.1f\n", student[i].id, student[i].name, student[i].course, student[i].score);
		len = strlen(tmp);
		for ( j=0 ; j<len ; ++j){
			fputc( tmp[j], stream );
			putchar(tmp[j]);
		}
	}
	
	fclose( stream );
	return 0;
}

运行结果:

20250001        zhangsan        Chinese 92.0

20250002        lisi    English 97.5

 

文件student.out中的内容如下:

程序分析:程序整体上比较简单,此处使用sprintf函数把需要写入文件的数据放到变量tmp中,然后使用fputc函数把tmp中的数据写入文件中,同时通过putchar函数把写入文件的字符显示到显示器上。

【例12.12】fputc函数的其他使用

 

12.2.3.4 fwrite函数

fwrite函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

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

函数传入值

buffer:指向要写入文件的数据块。

size:要写入文件的项目大小。

count:要写入文件的项目个数。

stream:文件指针,指向将要写入的文件流。

函数功能

将数据写入到文件流中。

函数返回值

1. 如果成功,则返回写字符的ASCII码值。

2. 如果失败,则返回EOF(-1)。

注:需要注意size和count的使用。

使用fwrite函数需要注意size和count的使用,每次既可以多个size作为一个count,写入文件中,也可以每次一个size,使用多个count写入文件中,当然每次还可以一个size作为一个count写入文件中,无论哪种方式本质上都是为了把需要的内容写入文件中。

【例12.13】使用fwrite函数存储羊驼图像

解题思路:使用时有两种方式,向上面所说的那样,代码中我们也将展示这两种方式的实现。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	char alpacas[1024];
	size_t i=0, len=0;
	FILE *stream = fopen( "alpacas.txt", "w+" );
	if( !stream ){
		printf( "The file alpacas.txt was not opened\n" );
		return -1;
	}
	
	strcat(alpacas, "┏┛┻━━━┛┻┓\n");
	strcat(alpacas, "┃|||||||┃\n");
	strcat(alpacas, "┃   ━   ┃\n");
	strcat(alpacas, "┃ ┳┛ ┗┳ ┃围观是一种态度\n");
	strcat(alpacas, "┃       ┃\n");
	strcat(alpacas, "┃   ┻   ┃\n");
	strcat(alpacas, "┃       ┃围观是为了提高知名度\n");
	strcat(alpacas, "┗━┓   ┏━┛\n");
	strcat(alpacas, "  ┃   ┃  \n");
	strcat(alpacas, "  ┃   ┃  \n");
	strcat(alpacas, "  ┃   ┃  \n");
	strcat(alpacas, "  ┃   ┃  \n");
	strcat(alpacas, "  ┃   ┗━━━┓\n");
	strcat(alpacas, "  ┃             ┣┓\n");
	strcat(alpacas, "  ┃围观专用宠物 ┃\n");
	strcat(alpacas, "  ┗┓┓┏━┳┓┏┛\n");
	strcat(alpacas, "   ┃┫┫ ┃┫┫\n");
	strcat(alpacas, "   ┗┻┛ ┗┻┛\n");
	
	len = strlen(alpacas);
	//方式1
	for ( i=0 ; i<len ; i++){
		fwrite(&alpacas[i], 1, 1, stream);
	}
	//方式2
	rewind(stream);
	fwrite(alpacas, 1, strlen(alpacas), stream);
	// 方式3
	rewind(stream);
	fwrite(alpacas, strlen(alpacas), 1, stream);
	
	fclose( stream );
	return 0;
}

运行结果:

程序分析:打开alpacas.txt文档中会出现一个羊驼图案,如果我们把39行和42行注释掉,那么就会在alpacas.txt文件中出现三个羊驼。废话不多说下面我们分析代码,首先我们把羊驼图像存放到字符数组alpacas中,然后使用使用strlen函数计算字符串长度,接着36行的fwrite函数是每次从alpacas中取一个字符存放到文件中,程序40行的fwrite函数是每次从alpacas中取一个字符,取strlen(alpacas)次,程序43行的fwrite函数是每次从alpacas中取strlen(alpacas)个字符,一共取一次,这三种fwrite函数的实现最终都能够把羊驼的完整图像存放到文件中去。

 

【例12.14】使用fwrite函数存储学生成绩至文档

解题思路:使用fwrite函数方法比较简单,此处我们也是使用fwrite常用的三种方法,

编写程序:

#include <stdio.h>
#include <stdlib.h>

typedef struct STUDENT{
	char id[9];
	char name[32];
	char course[32];
	float score;
}STUDENT;

int main( void )
{
	size_t i = 0, j = 0, len = 0;
	STUDENT student[2];
	char tmp[1024] = "\0";
	memset(&student[0], 0, sizeof(STUDENT));
	strcpy(student[0].id, "20250001");
	strcpy(student[0].name, "zhangsan");
	strcpy(student[0].course, "Chinese");
	student[0].score = 92.0;

	memset(&student[1], 0, sizeof(STUDENT));
	strcpy(student[1].id, "20250002");
	strcpy(student[1].name, "lisi");
	strcpy(student[1].course, "English");
	student[1].score = 97.5;

	FILE *stream = fopen( "student.txt", "w+" );
	if( !stream ){
		printf( "The file student.txt was not opened\n" );
		return -1;
	}
	
	//方式1
	for( i = 0; i < 2; ++i ){
		memset(tmp, 0, sizeof(tmp));
		sprintf(tmp, "%s\t%s\t%s\t%.1f\n", student[i].id, student[i].name, student[i].course, student[i].score);
		len = strlen(tmp);
		for ( j=0 ; j<len ; ++j){
			fwrite( &tmp[j], 1, 1, stream );
			putchar(tmp[j]);
		}
	}
	
	//方式2
	// rewind(stream);
	for( i = 0; i < 2; ++i ){
		memset(tmp, 0, sizeof(tmp));
		sprintf(tmp, "%s\t%s\t%s\t%.1f\n", student[i].id, student[i].name, student[i].course, student[i].score);
		len = strlen(tmp);
		fwrite(tmp, 1, len, stream);
		printf(tmp);
	}
	
	//方式3
	// rewind(stream);
	for( i = 0; i < 2; ++i ){
		memset(tmp, 0, sizeof(tmp));
		sprintf(tmp, "%s\t%s\t%s\t%.1f\n", student[i].id, student[i].name, student[i].course, student[i].score);
		len = strlen(tmp);
		fwrite(tmp, len, 1, stream);
		printf(tmp);
	}
	
	fclose( stream );
	return 0;
}

运行结果:

20250001        zhangsan        Chinese 92.0

20250002        lisi    English 97.5

20250001        zhangsan        Chinese 92.0

20250002        lisi    English 97.5

20250001        zhangsan        Chinese 92.0

20250002        lisi    English 97.5

 

文件student.txt中的内容如下:

程序分析:程序中前26行和之前一样都是数据预处理。程序中还是使用了fwrite的三种方式,如果去掉rewind函数的注释,就会在student.txt文件中重复输出三遍相同内容。

 

【12.15】fwrite的其他使用

 

 

 

12.2.4 读文件

我们已经知道,打开文件使用标准输入输出函数fopen,那么打开文件后我们想要读文件中的数据怎么办呢?我们有一系列函数可以使用,其中最常用的就是如下几个:fscanf函数、fgets函数、fgetc函数、fread函数。以下我们将会讲解一下这几个函数的使用。首先我们从fscanf函数开始吧!

12.2.4.1 fscanf函数

fscanf函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

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

函数传入值

stream:文件指针,指向要读取的文件流。

format :读取数据的格式。

函数功能

从一个流中执行格式化输入,fscanf遇到空格和换行是结束。

函数返回值

文件读取成功,返回读入的参数的个数。文件读取失败,返回EOF(-1)。

注意:fscanf函数遇到空格结束,fgets函数遇到空格不结束。

仅仅看看函数原型还是不能够完全理解这个函数,唯有实践才能够了解真相,正所谓实践才是检验真理唯一标准啊!

 

下面我们接着使用fscanf函数把之前使用各种写文件函数存入文件的数据读取出来,需要注意的是读文件函数尽量对应写文件函数,比如你使用fprintf写入文件,那么你就尽量使用fscanf读取出来,当然这些都不是固定的,你还可以使用其他函数读取,比如使用fgets函数等读取其他函数写入文件中的相应信息。

【例12.16】使用fscanf函数从文档中读取羊驼图案

解题思路:我们此处使用fscanf函数读取之前fprintf函数写入文件中的羊驼图案。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	char tmp[1024]="\0";
	FILE *stream = fopen( "alpacas.txt", "r" );
	if( !stream ){
		printf( "The file alpacas.txt was not opened\n" );
		return -1;
	}
	
	while(!feof(stream)){
		memset(tmp, 0, sizeof(tmp));
		fscanf(stream, "%s\n", tmp);
		printf("%s\n", tmp);
	}
	
	fclose( stream );
	return 0;
}

运行结果:

 

程序分析:在使用读取已经存在的alpacas.txt文件之前,需要把该文件使用notepad或者其他工具转换成ansi格式,转换方法可以自行百度。如果不转换的话,可能会解析出来乱码。程序第7行,我们使用“r”读的方式从文件中读取羊驼图像。然后使用fscanf函数从文件中读取数据,用feof函数判断文件流stream是否读到文件尾,如果不是尾部,循环读取,如果是尾部则结束读取,并使用printf函数输出到屏幕上。最后记得使用fclose函数关闭文件指针。

【例12.17】使用fscanf函数从学生成绩文件中读取学生信息

解题思路:我们使用fprintf实现学生成绩存储至文件中,使用fscanf从文件中读取学生成绩,并显示到界面上。

编写程序:

#include <stdio.h>
#include <stdlib.h>

typedef struct STUDENT{
	char id[9];
	char name[32];
	char course[32];
	float score;
}STUDENT;

int main( void )
{
	size_t i = 0;
	STUDENT tmp;

	FILE *stream = fopen( "student.out", "r" );
	if( !stream ){
		printf( "The file student.out was not opened\n" );
		return -1;
	}
	
	while(!feof(stream)){
		memset(&tmp, 0, sizeof(STUDENT));
		fscanf( stream, "%s\t%s\t%s\t%f\n", tmp.id, tmp.name, tmp.course, &tmp.score);
		printf("%10s, %10s, %10s, %3.1f\n", tmp.id, tmp.name, tmp.course, tmp.score);
	}
	fclose( stream );
	return 0;
}

运行结果:

  20250001,   zhangsan,    Chinese, 92.0

  20250002,       lisi,    English, 97.5

 

程序分析:程序4~9行建立学生信息的结构体,在main函数中使用memset函数把结构体数组分别进行初始化并赋值。然后以“w+”的方式创建一个名为student.out的文本文件,如果student.out文件存在,则清空文件中内容再新内容存至该文档中。接着使用fscanf函数把存至该文档中的内容读取出来(注意读取格式应与fprintf函数的存放格式相同)存放至tmp结构体变量中,并在显示器显示。最后记得使用fclose函数把文件指针关闭。另外,如果你不想创建以“.out”结尾的文件,也可以创建其他格式的文件,比如“.txt”结尾,都可以。

 

【例12.18】fscanf函数的简单使用。

解题思路:下面这是一段MSDN上的代码,首先使用fprintf函数写入文件数据,然后使用fscanf函数读取文件数据。这个fprintf我们后面将会介绍,此处暂时只是应用。

编写程序:

#include <stdio.h>
FILE *stream;
void main( void )
{
   long l;
   float fp;
   char s[81];
   char c;

   stream = fopen( "fscanf.out", "w+" );
   if( stream == NULL )
      printf( "The file fscanf.out was not opened\n" );
   else
   {
      fprintf( stream, "%s %ld %f%c", "a-string", 
               65000, 3.14159, 'x' );

      /* Set pointer to beginning of file: */
      fseek( stream, 0L, SEEK_SET );

      /* Read data back from file: */
      fscanf( stream, "%s", s );
      fscanf( stream, "%ld", &l );

      fscanf( stream, "%f", &fp );
      fscanf( stream, "%c", &c );

      /* Output data read: */
      printf( "%s\n", s );
      printf( "%ld\n", l );
      printf( "%f\n", fp );
      printf( "%c\n", c );

      fclose( stream );
   }
}

运行结果:

a-string

65000

3.141590

x

Press any key to continue

 

程序分析:程序第9行打开一个名为fscanf.out的只写文件。程序17行使用fseek函数定位文件指针的位置,该函数我们稍后介绍。程序19~22行是fscanf函数读取fscanf.out文件中的数据保存到对应的变量中,比如第19行读取文件指针stream所指位置的字符串数据,保存到对应的字符串变量中。其他的同样是这个道理,只不过保存的变量类型不同而已。

 

此处最好再添加一个示例代码

 

12.2.4.2 fgets函数

fgets函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

char *fgets( char *string, int n, FILE *stream );

函数传入值

string:字符型指针,指向用来存储所得数据的地址。

n:整形数据,指明存储数据的大小。

stream:文件指针,指向将要读取的文件流。

函数功能

从流中得到字符串。

函数返回值

  1. 如果成功,则返回第一个参数buf。
  2. 在读字符时遇到end-of-file,则eof指示器被设置,如果还没读入任何字符就遇到这种情况,则buf保持原来的内容,返回NULL。
  3. 如果发生读入错误,error指示器被设置,返回NULL,buf的值可能被改变。

注意:fscanf函数遇到空格结束,fgets函数遇到空格不结束。

fgets函数从指定流stream中读取一行,并把它存储到string所指向的字符串内。当读取n-1个字符时,或者读取到换行符时,或者到达文件末尾时,fgets函数就会停止读取,大家根据具体情况,具体使用。与读文件fgets函数对应的写文件函数就是之前说过的fputs函数。使用fputs函数写入文件的数据可以使用fgets函数读取,也可以使用其他函数读取,但是我建议优先选择fgets函数。

【例12.19】使用fgets函数从文档中读取羊驼图案

解题思路:我们使用fgets函数,每次从文件中读取一行数据。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	char alpacas[1024]="\0";
	FILE *stream = fopen( "alpacas.txt", "r" );
	if( !stream ){
		printf( "The file alpacas.txt was not opened\n" );
		return -1;
	}
	
	while(!feof(stream)){
		memset(alpacas, 0, sizeof(alpacas));
		fgets(alpacas, sizeof(alpacas), stream);
		printf(alpacas);
	}
	
	fclose( stream );
	return 0;
}

运行结果:

程序分析:本示例代码和其他代码整体上差别不大,首先使用memset函数把alpacas变量初始化,然后使用fget函数每次最多读取1024个字节,如果遇到换行或者文件结尾处,则提前结束。程序会把读取的字符串存放到alpacas中。另外,fgets函数会读取文档中的换行符。

 

【例12.20】使用fgets函数从学生成绩文件中读取学生信息

解题思路:使用fgets函数把输入文件中的内容一行一行的读取出来,如果要确定一行里面的具体内容,需要使用字符串解析函数解析字符串。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	size_t i = 0;
	char student[1024] = "\0";
	memset(student, 0, sizeof(student));

	FILE *stream = fopen( "student.txt", "r" );
	if( !stream ){
		printf( "The file student.txt was not opened\n" );
		return -1;
	}
	
	while(fgets( student, sizeof(student), stream ) != NULL){
		printf("%s", student);
		memset(student, 0, sizeof(student));
	}
	
	fclose( stream );
	return 0;
}

运行结果:

20250001        zhangsan        Chinese 92.0

20250002        lisi    English 97.5

 

程序分析:如果程序中fgets函数读取到文件末尾时,没有数据了就会返回NULL,所以可以通过判断fgets函数是否为NULL,来判断文件是否读取结束。之后使用memset把之前读取的数据清空。最后使用fclose函数关闭文件指针。

 

 

【例12.21】fgets函数的简单使用。

解题思路:这段代码是从MSDN上截取出来的。很简单。

编写程序:

#include <stdio.h>
void main( void )
{
   FILE *stream;
   char line[100];

   if( (stream = fopen( "fgets.c", "r" )) != NULL )
   {
      if( fgets( line, 100, stream ) == NULL)
         printf( "fgets error\n" );
      else
         printf( "%s", line);
      fclose( stream );
   }
}

运行结果:

#include <stdio.h>

void main( void )

{

   FILE *stream;

   char line[100];

 

   if( (stream = fopen( "fgets.c", "r" )) != NULL )

   {

           while( fgets( line, 100, stream ) != NULL)

           {

                        printf( "%s", line);

           }

       printf("\n");

   }

   fclose( stream );

}

Press any key to continue

 

程序分析:在写这个程序时需要注意的是把对应的C文件命名为fgets.c。这样我们在打开文件fgets.c时,就不会出现文件打开错误。

 

 

【例12.22】

 

 

 

12.2.4.3 fgetc函数

fgetc函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

int fgetc( FILE *stream );

函数传入值

stream:文件指针,指向将要读取的文件流。

函数功能

从流中读取一个字符。

函数返回值

1. 如果成功,则返回读取的字符。

2. 如果读到EOF,则表示文件出错或者读到文件的结尾。

fgetc函数从指定流stream中读取一个字符,并返回读取的字符。当到达文件末尾时,fgetc函数就会停止读取。与fgetc函数对应的是fputc函数,如果使用fputc函数写入文件数据,则可以使用fgetc函数读取出来,同样使用其他文件读取函数也是可以的。

 

【例12.23】使用fgetc函数从文档中读取羊驼图案

解题思路:此处使用fgetc函数从文档中一个字符一个字符读取羊驼图案。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	int alpacas='\0';
	FILE *stream = fopen( "alpacas.txt", "r" );
	if( !stream ){
		printf( "The file alpacas.txt was not opened\n" );
		return -1;
	}
	
	while(!feof(stream)){
		alpacas = fgetc(stream);
		printf("%c", alpacas);
	}
	
	fclose( stream );
	return 0;
}

运行结果:

程序分析:我们使用fgetc函数读取文档中的羊驼图像,在程序第6行我们定义了一个整型变量alpacas,初始化为字符‘\0’,因为本质上字符就是一个整数,所以在第15行我们把整数按照字符输出,就输出了羊驼的图像。整体很简单。

 

【例12.24】使用fgetc函数从学生成绩文件中读取学生信息

解题思路:使用fgetc函数把存入文档的学生信息一个字符一个字符读取出来,通过该函数可以对读取的字符做一定的处理,比如把字符转换成整数等。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	int student = '\0';

	FILE *stream = fopen( "student.txt", "r" );
	if( !stream ){
		printf( "The file student.txt was not opened\n" );
		return -1;
	}
	
	while( (student = fgetc(stream)) != EOF ){
		printf("%c", student);
	}
	
	fclose( stream );
	return 0;
}

运行结果:

20250001        zhangsan        Chinese 92.0

20250002        lisi    English 97.5

 

程序分析:因为fgetc函数如果成功返回读取字符的字符,失败则返回EOF,所以程序第14行通过读取的字符存储到student中,并判断student是否等于EOF,如果等于EOF则表示程序已经读完文档。

 

【例12.25】

解题思路:

编写程序:

运行结果:

 

程序分析:

 

12.2.4.4 fread函数

fread函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

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

函数传入值

buffer:读取的字符数据

size:一个项目大小,以字节为单位。

count:要读取的最大项目个数。

stream:文件指针,将要读取的文件流。

函数功能

从流中读取字符数据。

函数返回值

1. 如果成功,则返回实际读取到的项目数(小于等于count)。

2. 如果失败或者读取到文件结尾,则返回0。

fread函数从文件中读取count个大小为size的字符数据,读取的数据存放到buffer缓冲区,此处理解和fwrite函数一致,只不过一个是写入文件,一个是读取文件而已。与读文件fread函数对应的是写文件fwrite函数,可以使用fread函数读取fwrite函数写入文件的数据。

 

【例12.26】使用fread函数从文档中读取羊驼图案

解题思路:我们使用fread函数的三种方式来读取fwrite函数三种方式写入文件中羊驼图案,让大家了解如何使用fread函数。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	char alpacas[1024]="\0", ch;
	size_t i=0, len=0;
	FILE *stream = fopen( "alpacas.txt", "r" );
	if( !stream ){
		printf( "The file alpacas.txt was not opened\n" );
		return -1;
	}
	
	//方式1
	while ( !feof(stream) ){
		fread(&ch, 1, 1, stream);
		printf("%c", ch);
	}
	
	// 方式2
	rewind(stream);
	fread(alpacas, 1, 1024, stream);
	printf("%s", alpacas);
	
	// 方式3
	memset(alpacas, 0, sizeof(alpacas));
	rewind(stream);
	fread(alpacas, 1024, 1, stream);
	printf("%s", alpacas);
	
	fclose( stream );
	return 0;
}

运行结果:

 

┏┛┻━━━┛┻┓

┃|||||||┃

┃   ━   ┃

┃ ┳┛ ┗┳ ┃围观是一种态度

┃       ┃

┃   ┻   ┃

┃       ┃围观是为了提高知名度

┗━┓   ┏━┛

┃   ┃  

┃   ┃  

┃   ┃  

┃   ┃  

┃   ┗━━━┓

┃             ┣┓

┃围观专用宠物 ┃

┗┓┓┏━┳┓┏┛

┃┫┫ ┃┫┫

┗┻┛ ┗┻┛

 

┏┛┻━━━┛┻┓

┃|||||||┃

┃   ━   ┃

┃ ┳┛ ┗┳ ┃围观是一种态度

┃       ┃

┃   ┻   ┃

┃       ┃围观是为了提高知名度

┗━┓   ┏━┛

┃   ┃  

┃   ┃  

┃   ┃  

┃   ┃  

┃   ┗━━━┓

┃             ┣┓

┃围观专用宠物 ┃

┗┓┓┏━┳┓┏┛

┃┫┫ ┃┫┫

┗┻┛ ┗┻┛

┏┛┻━━━┛┻┓

┃|||||||┃

┃   ━   ┃

┃ ┳┛ ┗┳ ┃围观是一种态度

┃       ┃

┃   ┻   ┃

┃       ┃围观是为了提高知名度

┗━┓   ┏━┛

┃   ┃  

┃   ┃  

┃   ┃  

┃   ┃  

┃   ┗━━━┓

┃             ┣┓

┃围观专用宠物 ┃

┗┓┓┏━┳┓┏┛

┃┫┫ ┃┫┫

┗┻┛ ┗┻┛

程序分析:该程序使用fread函数读取fwrite函数写入文件的羊驼图像,之前fwrite函数使用三种方式写入羊驼图像至文件中,现在fread函数使用三种方式读取羊驼图像。fread函数的使用类似于fwrite函数,大家可以参考fwrite函数。

 

【例12.27】使用fread函数从学生成绩文件中读取学生信息

解题思路:此处使用fread函数读取存入文档中的学生信息。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	char student[1024] = "\0", ch;

	FILE *stream = fopen( "student.txt", "r" );
	if( !stream ){
		printf( "The file student.txt was not opened\n" );
		return -1;
	}
	
	//方式1
	while( fread( &ch, 1, 1, stream ) > 0 ){
		printf("%c", ch);
	}
	
	//方式2
	rewind(stream);
	// fseek(stream, 0L, SEEK_SET); //和rewind函数功能相同
	while( !feof(stream) ){
		fread( &ch, 1, 1, stream );
		printf("%c", ch);
	}
	
	//方式3
	// rewind(stream);
	fseek(stream, 0L, SEEK_SET); //和rewind函数功能相同
	fread(student, 1024, 1, stream);
	printf("%s", student);
	
	fclose( stream );
	return 0;
}

运行结果:

20250001        zhangsan        Chinese 92.0

20250002        lisi    English 97.5

20250001        zhangsan        Chinese 92.0

20250002        lisi    English 97.5

 

20250001        zhangsan        Chinese 92.0

20250002        lisi    English 97.5

 

程序分析:这个程序使用fread函数的多种方法读取文件中的学生成绩信息,大家可以学习了解。大家自己运行程序会发现输出的结果有点异样,你试试看看能不能发现,并且找到其中的原因。

 

【例12.28】

 

 

 

12.3 文件的随机读写操作

12.3.1 fseek函数和ftell函数

fseek函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

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

函数传入值

stream:指向要定位的文件指针。

offset:相对于origin对应位置的偏移量。

origin:起始位置。

  1. SEEK_CUR:指当前文件指针的位置。
  2. SEEK_SET:指向文件开始的位置。
  3. SEEK_END:指向文件的末尾。

函数功能

移动文件指针到指定位置。

函数返回值

1. 如果成功,则返回0。

2. 如果失败,则返回-1。

注:如果失败返回-1的同时,会设置error的值,可以使用perror()函数输出错误。

 

ftell函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

long ftell( FILE *stream );

函数传入值

stream:指向要定位的文件指针。

函数功能

用于得到文件位置指针当前位置相对文件首的偏移字节数。

函数返回值

1. 如果成功,则返回当前位置相对于文件头的偏移量。

2. 如果失败,则返回-1。

注:如果失败返回-1的同时,会设置error的值,可以使用perror()函数输出错误。

 

fseek函数用于文件位置的设定,就是把文件指针定位到指定位置。可以通过起始位置设定偏移量,起始位置有三个:一个是文件开头,一个是当前指针位置,一个是文件末尾。通过这三个位置来设定偏移量,进而获取指定位置的数据。而ftell函数可以可以当你不知道当前指针位置时,通过该函数获取文件相对文件开头的偏移量。

【例12.29】fseek定位文件位置,ftell获取当前文件位置。

解题思路:使用fseek定位要读写文件的开始位置,用ftell获取当前文件指针指向的位置。通过获取的位置可以定位到文件的任意位置来读写文件。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	char info[1024] = "\0", ch;
	
	FILE *stream = fopen( "file.txt", "r" );
	if( !stream ){
		printf( "The file student.txt was not opened\n" );
		return -1;
	}
	
	while( !feof(stream) ){
		fread( &ch, 1, 1, stream );
		printf("%c", ch);
	}
	//定位到文件开头再读一遍文件
	fseek(stream, 0L, SEEK_SET); 
	while( !feof(stream) ){
		fread( &ch, 1, 1, stream );
		printf("%c", ch);
	}
	
	fseek(stream, 0L, SEEK_END); //指向文件末尾
	long end = ftell(stream);
	long middle = end / 2; //获取文件的中间位置
	fseek(stream, middle, SEEK_SET); //定位到文件的中间位置
	while( !feof(stream) ){
		fread( &ch, 1, 1, stream );
		printf("%c", ch);
	}
	
	fseek(stream, 0L, SEEK_SET); //指向文件末尾
	fread(info, middle - 1, 1, stream);
	printf("%s\n", info);
	
	fclose( stream );
	return 0;
}

运行结果:

ABCDEFGHIJKLMNOPQRSTUVWXYZ

abcdefghijklmnopqrstuvwxyz

 

ABCDEFGHIJKLMNOPQRSTUVWXYZ

abcdefghijklmnopqrstuvwxyz

 

 

abcdefghijklmnopqrstuvwxyz

 

ABCDEFGHIJKLMNOPQRSTUVWXYZ

 

程序分析:使用fopen函数打开文件时默认指向文件开头,程序14~17行循环读取文件信息,每次读取一个字节的数据,直到文件末尾。然后19~23行使用fseek函数中的SEEK_SET标志,把文件指针重新指向文件开头位置,完整读取文件内容至文件末尾。程序25行同样使用fseek函数中的SEEK_END定位到文件末尾,使用ftell函数得到当前位置相对文件首的偏移字节数,end变量表示文件总长度,我们取整个文档的一半,程序28~32行和34~40行,分别读取文件的后一半数据和前一半数据。

 

12.3.2 rewind函数

rewind函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

void rewind( FILE *stream );

函数传入值

stream:文件指针。

函数功能

将文件内部的位置指针重新指向一个流的开头。

函数返回值

无返回值。

rewind函数能够把指向任意位置的文件指针,重新设置为指向文件开始位置。

【例12.30】rewind函数重置文件指针至文件开头位置

解题思路:本例使用rewind函数来重新设置文件指针至文件开始位置,无论文件指针在任何位置都能够把它重新设置到文件开头。

编写程序:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
	char info[1024] = "\0", ch;
	long seat = 0, old_seat = 0;
	
	FILE *stream = fopen( "file.txt", "r" );
	if( !stream ){
		printf( "The file student.txt was not opened\n" );
		return -1;
	}
	fseek(stream, 0L, SEEK_END); //指向文件末尾
	seat = ftell(stream); old_seat = seat / 2;
	printf("%d\n", seat);
	
	rewind(stream);//重新定位到文件开头
	seat = ftell(stream);
	printf("%d\n", seat);
	
	fseek(stream, old_seat, SEEK_SET); //定位到文件的中间位置
	seat = ftell(stream);
	printf("%d\n", seat);
	
	rewind(stream);//重新定位到文件开头
	seat = ftell(stream);
	printf("%d\n", seat);
	
	fclose( stream );
	return 0;
}

运行结果:

56

0

28

0

 

程序分析:程序第14行把文件定位到文件尾,通过ftell函数获取当前位置相对文件开头的偏移量。然后第18行使用rewind函数重新定位到文件开始位置,通过ftell函数获取文件位置并输出查看。在第22行把文件指针指向文件的中间位置,再次使用26行的rewind函数定位文件的开头,并且每一步都通过ftell函数获取对应位置并输出。

 

12.3.3 fsetpos函数和fgetpos函数

fsetpos设置文件指针位置,fgetpos函数获取文件指针位置。

fsetpos函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

int fsetpos( FILE *stream, const fpos_t *pos );

函数传入值

stream:文件指针。

pos:定位到指定pos的位置。

函数功能

将文件指针定位到pos指定的位置上。

函数返回值

1. 如果成功,则返回0。

2. 如果失败,则返回非0。

 

fgetpos函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

int fgetpos( FILE *stream, fpos_t *pos );

函数传入值

stream:文件指针。

pos:获取当前访问指针位置信息。

函数功能

在pos所指的位置放置一个fpos_s值,这个值描述了文件中的一个位置。

函数返回值

1. 如果成功,则返回0。

2. 如果失败,则返回非0。

#include <stdio.h>

void main( void )

{

   FILE   *stream;

   fpos_t pos;

   char   buffer[20];

 

   if( (stream = fopen( "fgetpos.c", "rb" )) == NULL )

      printf( "Trouble opening file\n" );

   else

   {

      /* Read some data and then check the position. */

      fread( buffer, sizeof( char ), 10, stream );

      if( fgetpos( stream, &pos ) != 0 )

         perror( "fgetpos error" );

      else

      {

         fread( buffer, sizeof( char ), 10, stream );

         printf( "10 bytes at byte %ld: %.10s\n", pos, buffer );

      }

 

   /* Set a new position and read more data */

   pos = 140;

   if( fsetpos( stream, &pos ) != 0 )

      perror( "fsetpos error" );

 

   fread( buffer, sizeof( char ), 10, stream );

   printf( "10 bytes at byte %ld: %.10s\n", pos, buffer );

   fclose( stream );

   }

}

 

12.4 文件出错检查

 

FEOF文件出错检查

文件结束检测函数feof函数调用格式:feof(文件指针)

功能:判断文件是否处于文件结束位置,如果文件结束,则返回值为1,否则为0。

 

ferror函数

读写文件出错检测函数

ferror函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

int ferror( FILE *stream );

函数传入值

stream:文件指针。

函数功能

测定给定流stream的错误标志符。

函数返回值

如果文件流中没有发生错误,则返回0。否则返回非0。

clearerr函数

文件出错标志和文件结束标志置0。

功能:该函数用于清除出错标志和文件结束标志,使它们为0值。

clearerr函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

void clearerr( FILE *stream );

函数传入值

stream:文件指针。

函数功能

清除结束文件和错误指标给定的流。

函数返回值

这个函数应该不会失败,并没有设置外部变量errno。但是如果它检测到它的参数不是一个有效的流,它必须返回-1,并设置errno为EBADF。

#include <stdio.h>

int main()

{

   FILE *fp;

   char c;

   fp = fopen("file.txt", "w");

   c = fgetc(fp);

   if( ferror(fp) )

   {

      printf("Error in reading from file : file.txt");

   }

   clearerr(fp);

   if( ferror(fp) )

   {

      printf("Error in reading from file : file.txt");

   }

   fclose(fp);

   return(0);

}

 

 

 

perror函数

perror函数原型定义如下:

所需头文件

#include <stdio.h>

函数原型

void perror( const char *string );

函数传入值

string:要输出消息。

函数功能

将上一个函数发生错误的原因输出到标准设备(stderr)。

函数返回值

这个函数应该不会失败,并没有设置外部变量errno。但是如果它检测到它的参数不是一个有效的流,它必须返回-1,并设置errno为EBADF。

注:在库函数中有errno变量,每个errno值对应着以字符串表示的错误类型。当你调用“某些”函数出错时,该函数已经重新设置了errno的值。perror函数只是将你输入的一些信息和现在的errno所对应的错误一起输出。

另外errno是一个宏。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值