chapter 10 对文件的输入输出

10.1 对文件的有关的基本知识

10.1.1 什么是文件

一、背景知识
C语言编写的程序是源程序,计算机不能直接识别和执行高级语言所写的指令,必须用编译器把C源程序翻译成二进制形式的目标程序,然后再将目标程序与系统的函数库以及其他目标程序连接起来,形成可执行程序。
将一个C程序编写好之后,要在计算机上运行,必须经历以下几个阶段:
在这里插入图片描述准备阶段(文件名约定)
不同的环境具有不同的命名约定,这里以Windows系统为例
(1)C源代码的以“.c”为后缀,源文件保存程序的定义,也称为定义文件;
(2)有#include指令包含到C源代码的文件称为头文件,用来保存文件的声明,以”.h”为后缀;
(3)C源程序编译后形成的文件称为目标文件,以”.obj”为后缀;
(4)将目标文件进行连接处理后得到的文件称为可执行文件,以”.exe”为后缀。

一、预编译(预处理):(.c/.cpp)

预编译过程主要处理那些源代码文件中的以“#”开始的预处理命令。比如#include

1.处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。

2.删除掉所有注释"//“和”/* */".

二、编译(.i)

编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析、及优化后生产相应的汇编代码文件,这个过程往往是我们所说的整个程序构建的核心部分,也是最复杂的部分之一。也就是查错。主要错误有:语法错误,函数使用错误(只检查函数有没有声明,链接检查函数有没有实现的部分)。

三、汇编(.asm)

汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。汇编器的汇编过程相对于编译器来讲比较简单,他没有复杂的语法,也没有语义,也不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译就可以了。

四、链接(obj.)

将编译后得到的所有的目标文件连接装配起来,再与函数库相连接成一个整体,生成一个可供计算机执行的可执行程序(.exe)。

文件主要可以分类为两种,一种称之为程序文件,还有一种称为数据文件;前面我们在第一章节介绍了代码是如何编译运行的,里面我们介绍了各种.c.exe文件。这个文件的内容是程序代码的叫做程序文件。
———————————————————————————————————
分割线
———————————————————————————————————

但是对于一个完整的程序,还有一个关键的东西,那就是数据。数据文件也属于文件的一部分,但不是程序文件。数据文件是来储存数据的。

咱们快来看看数据文件
什么是文件?我更想把文件理解只是一种数据流通的形式或者媒介。他不是数据本身。
文件与数据:举个例子,如果说沙漠中所有的沙子是漫漫的数据,那么说如果说你想把这些沙子运到别处你只能通过某种容纳物件,比如沙袋来运送。而这里所说的沙袋就是咱们说的文件。而沙子的往来运送,可以理解为是数据得输入输出。可以称之为数据流。数据的流动。
在这里插入图片描述输入输出(Input output,IO)是指程序(内存)与外部设备(键盘、显示器、磁盘、其他计算机等)进行交互的操作。几乎所有的程序都有输入与输出操作,如从键盘上读取数据,从本地或网络上的文件读取数据或写入数据等。通过输入和输出操作可以从外界接收信息,或者是把信息传递给外界。
数据在磁盘或者其他外部设备和内存之间传递的过程叫做文件流,类似水从一个地方流动到另一个地方。数据从外部设备到内存的过程叫做输入流,从内存到外部设备的过程叫做输出流。

注意:所有保存在磁盘的文件都要载入内存才能处理,所有的数据必须写入磁盘中的文件才不会丢失。

咱们宏观以为数据就是一段段代码,其实这一段段代码都是有一个个字符组成得无行数限制得一行,就像一条蜿蜒不绝得河流,这条河流流动得方向,开始结束,只受到程序的控制。
但是咱们正常范围理解的文件,也即是广义上理解为,书上说:所有储存在外部介质中数据的集合都可以叫做文件。这相当于沙子加上沙包两者的综合。我们称之为一个文件。

10.1.2 文件名

文件名包括三部分;

D:\CC\temp\file1.dat

其中包括文件路径,文件名主干,还有文件后缀。这是一个完整的文件名。我们生活里说的文件名通常是这里的文件名主干。但是你要知道真正的文件名应该是什么。

10.1.3文件的分类

存储器包括内部存储器(内存)、外部存储器(外存)、寄存器。

内存包括只读存储器(ROM,Read Only memory)(只读,断电后数据保留)、随机存取存储器(RAM,Random Access Memory)(主存)(内存条)(可读可写,断电后数据丢失)、高速缓冲存储器(CACHE)。
外存包括磁盘、光盘、U盘等。

磁盘分硬盘和软盘。软盘容量小,在光盘时代被淘汰。光盘写入数据需光盘刻录机,在U盘时代被淘汰。
硬盘分为机械硬盘、固态硬盘、混合硬盘。

寄存器是CPU(Central Processing Unit)的组成部分。
从功能方面来看,CPU内部由寄存器,控制器,运算器和时钟四部分构成,各部分之间由总线上的电流信号相互连通。总线分数据总线、控制总线、状态总线三种。

从组织形式看,文件可以分为ASCALL文件,还有二进制文件。ASCALL文件他的二进制是一个字节一个字节的排序。

文本文件(ASCALL文件)
例如1000000110001...00110000   00110000   00110000  00110000
1          0             0          0         0

而对于二进制文件:

映像文件(二进制文件)
文本文件
例如:10000
00000000000000000010011100010000

优缺点:

ASCALL文件:
优点:一个字节代表一个字符,便于对字符进行处理,也便于输出字符。
缺点:占内存多,花费转换时间(电脑最终只识别二进制代码,需要转化为二进制文件)
 二进制文件:
 节省空间 时间;二进制文件使用的时候原封不动的输入到内存上去,不需要转化,不想ASCALl文件需要转化成二进制才能被处理。这样说用二进制比较方便。

10.1.4 文件缓冲区

在这里插入图片描述C 采用“缓冲文件系统”处理数据。
就是说在内存中开辟一个缓冲区。包括输出缓冲区,输入缓冲区。还开辟了一个程序数据区。就是说如果想要把数据输入到数据区。需要先过渡一下。数据先一个一个存放到缓冲区,知道缓冲区存放满之后,一个个送到数据区。同样说输出,就是把数据一个个输出到缓冲区,然后就是在一次性输出到外存中。
缓冲区的个人理解
这里所说的缓冲区指的是为标准输入与标准输出设置的缓冲区,为什么要设置一个标准输入缓冲区主要是从效率上来考虑的,CPU处理数据,一般会在磁盘中或者缓冲区中去读取,相比于直接去磁盘去读写,在缓冲区读写会快很多,所以尽量把数据都放在缓冲区中,这样就可以是计算机不用区磁盘中读写,如果不设缓冲区会降低cpu的效率!

10.1.5 文件类型指针

每一个文件都会在内存中开辟一个文件信息区域,用来存放这个文件的某些信息。然后就可以通过访问存储在内存中的文件信息区域,找到存储在外部介质中的文件了。而文件信息区域的空间是通过结构体定义的。

typedef struct 
{
	short level; //缓冲区满和空的程度
	unsigned flags;//文件状态标志
	char fd;//文件描述符
	unsigned char hold;//如缓冲区无内容不读取字符
	short bsize;//缓冲区的大小
	unsigned char *buffer;//数据缓冲区的位置
	unsigned char *curp;//指针当前的指向
	unsigned istemp;//临时文件的指示器
	short token;//用于有效的检查
}FILE;

定义了一个结构体,这样内存中就开辟了一段储存空间,这段储存空间就充当文件信息区。
注意,这段储存空间实在stdio.h头文件中就定义好的。不需要要你去定义,而且变量名字规定是FILE。你可以直接使用就可以了;
说到这里,你坑定会问,怎么对这段结构体进行初始化,就是输入某一个文件的相关信息:这些信息是打开文件时由系统自动输入的,用户不必过问。
还有一个问题就是,如何访问这文件信息区域。
前面学过了指向结构体的指针。通过使用结构体的地址,其指向该结构体去访问它。不能直接通过变量去访问。

FILE *f1;

在这里插入图片描述

10.2 打开文件和关闭文件

读写文件时需要打开文件,读写完毕后需要关闭文件;
打开文件:建立相应得信息区域,文件缓冲区,定义一个指针变量指向它;
关闭文件:撤销文件信息区域,文件缓冲区,是这个指针变量不再指向;

10.2.1 用fopen函数打开数据文件

打开文件要用到打开文件的函数才能打开文件,该函数是在函数库中定义好的,你可以直接调用就可以了。调用方式如下:
fopen(文件名,使用文件方式)

FILE *fp;
fp=fopen("a1","r");
调用函数会有一个返回值,这个返回值就是具有该文件名的文件相对应的在内存中文件信息区域的首地址;

从上面可以看出,使用打开文件的函数,首先需要
a)打开文件的名字,准备访问的文件名
b)使用文件的方式是只读,还是只写,还是读写等
c)定义一个指针变量,把返回值赋值给该指针变量,这样指针变量就指向了该目标文件。

使用文件方式:
读:输入数据这个输入数据是从该文件输出,输入到程序

写:输出数据这个输出数据是从程序中输出数据,输入到文件
在这里插入图片描述1)“r” 向一个已经存在,有数据的文件 ,从文件输出,输入数据到程序。如果指定文件不存在,那么显示出错。
2)“w” 程序输出数据,输入到文件,相当于文件写数据,如果没有该文件,则新建一个文件。如果有该文件,则删除该文件,然后新建一个新文件。
3)“a” 追加;在文件末尾添加数据,就是说程序输出数据,输入到文件,但是不删除原有数据,在原有数据的末尾接着写,这就是追加。如果文件不存在,显示出错。
4)“r+,w+,a+”就是既可以读又可以写,
首先是“r+”这个文件一般已经存在。它可以从文件输出数据,输入到程序中。输出完数据以后,也可以接受来自程序的数据。
然后是“w+”,是新建一个文件,这个文件既可以读,也可以写;
最后是“a+”,是程序输出数据,输入到文件,文件的数据可以不删除,直接到末尾处接着写。输入完了之后,可以拿去输出。

在这里插入图片描述5)从上面可以看出,如果说读写出现错误;就是打开一个文件出现错误;可能是一下几个原因;

用r方式打开一个并不存在的文件;
磁盘出现故障;
磁盘已满,无法建立一个新的文件;

此时调用这个函数出现了错误,函数无法返回你一个文件的具体地址,他会返回你一个空指针NULL;(在stdio.h头文件中NULL已经被定义成0if((fp=fopen("file 1","r"))==NULL)
{
	printf("cannot open this file\n");
	exit(0);//关闭所有文件,终止正在执行的程序
}

6)咱们光靠fopen打开文件还不行,数据还不能随意从文件和程序中间传输,如果想让数据能够传输,还需要能够传输的渠道。系统自带三种流文件,他们不需要通过fopen打开,只要程序一运行,他们会自动打开。
他们分别称为:标准输入流,标准输出流,标准出错输出流…
也分别在头文件中指定了指针变量去指向他们:
在这里插入图片描述
文件是数据源的一种,最主要的作用是保存数据。在操作系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。例如,通常把显示器称为标准输出文件,printf就是向这个文件输出,把键盘称为标准输入文件,scanf就是从这个文件获取数据。

10.2.2 用fclose 函数关闭数据文件;

为什么要关闭文件,当建立了一个文件,你就建立缓冲区和程序区域的流动。举例子,当输入到程序区域时候,你只有缓冲区满了才可以。如果你不关闭文件,直接关闭程序,你的缓冲区的数据将会丢失。所以需要先关闭文件,也就是先把缓冲区的数据转到数据区域的时候,再把程序关闭。
怎么调用关闭函数:
fclose(文件指针)

fclose(fp);
切断指针的指向,关闭文件。

10.3 顺序读写数据文件

顺序读写就是按照数据在文件的物理顺序,读文件的时候,自上而下。写文件的时候也是如此。对文件的顺序读写一般需要借助库函数实现。

10.3.1怎么向文件读写字符

在这里插入图片描述看上面库函数的函数;

fgetc(fp);//fp:指向所需要操作的文件;文件读出
fputc(fp);//fp:指向所需要操作的文件;写入文件

举个例子:

/*从键盘上输入一些字符,逐个把他们送到磁盘上去,
直到用户输入一个“#”为止*/ 
#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE *fp;//定义了一个指针变量,这个指针变量是用来指向文件的 
	char ch,filename[10];//定义了一个字符变量,和一个字符数组储存文件名 
	printf("please enter by-used  filename: ");
	scanf("%s",filename);
	if((fp=fopen(filename,"w"))==NULL)
	{
		printf("无法打开文件\n");
		exit(0);
	}
	
	//ch=getchar();//用来接受最后的回车符号 
	printf("请输入一个准备储存到磁盘的字符串(以#结束):");//输入一段字符串 
	ch=getchar();//接受字符串第一个字符; 
	while(ch!='#')
	{
		fputc(ch,fp);//写入文件第一个字符 
		putchar(ch);//输出第一个字符到文件 
		ch=getchar();//得到下一个字符 
	 } 
	 fclose(fp);//字符串全部输入完毕 ,关闭文件 
	 putchar(10);//输出换行符 
	 return 0;
} 

在这里插入图片描述
exit函数的头文件是stdlib.h;
在这里插入图片描述能在磁盘中看到。

再看一个例子

/*将一个磁盘文件的信息复制到宁外一个磁盘文件上;
今让之前的file.dat 的数据放置到file2.dat 上。*/ 
#include <stdio.h>
#include <stdlib.h>
int main()
{
	FILE *in,*out;//定义了两指针变量 
	char ch,infile[10],outfile[10];
	printf("输入读入文件的名字:");
	scanf("%s",infile);
	printf("输入输出文件的名字:");
	scanf("%s",outfile);
	if((in=fopen(infile,"w"))==NULL)
	{
		printf("无法打开文件\n");
		exit(0);
	}//打开文件1,判断是否有错误 
	if((out=fopen(outfile,"w"))==NULL)
	{
		printf("无法打开文件\n");
		exit(0);
	}//打开文件2,判断是否有错误 
	while (!feof(in))//feof是检查所指向的是否结束 
	{
		ch=fgetc(in);
		fputc(ch,out);
		putchar(ch);
	}
	putchar(10);
	fclose(in);
	fclose(out);
	return 0;
} 

这里面函数是feof函数。
他是用来判断所指向的文件是否结束;他是怎么判断一个文件是否结束的呢;
在访问磁盘文件时候,为了知道访问到了第几个字节的时候,系统用文件“文件读写位置标记”来表示访问的位置,当访问下一个字节时候,标记也会到下一个。所以说判断一个文件是否访问完成,可以根据他的访问位置是否在文件的末尾。

feof(in);结束:1;没有结束:0

10.3.2 怎么向文件读写一个字符串

在这里插入图片描述

函数定义:char *fgets (char *str,int n,FILE*fp);指针函数,返回的是地址。
调用函数:fgets(str,n,fp);
定义了一个数组,其中包含n个元素,最后是字符串结束标志“\0”,所以说真正读入文件的字符串共包含n-1个字符。
为什么用char :???返回值是字符型的数据,
函数定义:int fputs(char*str,FILE*fp);定义一个整形函数,返回值是整型
调用函数:fputs("china",fp);括号内第一个位置可以是字符串常量,字符数组名,字符型指针;数组里的\0不输出到文件中。

看一个例子,从键盘读入若干个字符串,对他们按照字母大小的顺序排序,
然后把排序好的字符串送到磁盘的文件中保存

 /*从键盘读入若干个字符串,对他们按照字母大小的顺序排序,
 然后把排序好的字符串送到磁盘的文件中保存*/ 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 int main()
 {
 	FILE *fp;
 	char  str[3][10],temp[10];
 	int i,j,k,n=3;
 	printf("ENTER strings:\n");

 	//字符串赋值
 	for(i=0;i<n;i++)
 		gets(str[i]);//输入一个字符串给数组 同时顺便返回一个函数值;而该函数值就是这个数组的地址;str[i]是第一行的地址。

 	//开始选择法排序
 	for(i=0;i<n-1;i++)//循环一次 循环二次
	 {
	 	k=i;
	 	for(j=i+1;j<n;j++)//循环两次  循环一次
	 		if(strcmp(str[k],str[j])>0) k=j;//比较字符串,找出最小的那个字符串的标记给K; 
	 	if(k!=i)//字符串互换 
	 	{
	 		strcmp(temp,str[i]);
	 		strcpy(str[i],str[k]);
	 		strcpy(str[k],temp);
		 }
		
     }


     //打开了一个文件
	if((fp=fopen("D:\\CC\\wdw.dat","w"))==NULL)
	{
		printf("can't open file !\n");
		exit(0);
	} 

	//输出字符串
	printf("\nthe new sequence :\n ");
	for(i=0;i<n;i++)
	{
		fputs(str[i],fp);
		fputs("\n",fp);
		printf("%s\n",str[i]);
	}
	return 0;
	 
}
fp=fopen("D:\\CC\\wdw.dat","w")
去指定目录下打开一个文件,写入数据;其实本来因改写成
fp=fopen("D:\CC\wdw.dat","w");这是正常的文件路径
但是\在C语言中包含转义的意思,所以为了避免重复,就用\\代替。

现在说怎么把这个字符串读出。

 /*从键盘读入若干个字符串,对他们按照字母大小的顺序排序,
 然后把排序好的字符串送到磁盘的文件中保存,现在读出它*/ 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 int main()
 {
 	FILE *fp;
 	char  str[3][10];
 	int i=0;  	
     //打开了一个文件
	if((fp=fopen("Dstring.dat","r"))==NULL)
	{
		printf("can't open file !\n");
		exit(0);
	} 

	//输出字符串
	while(fgets(str[i],10,fp)!=NULL)
	{
		printf("%s",str[i]);
		i++;
	}
	fclose(fp);
	return 0;
	 
}

ps
本书是谭浩强的书,他在书中使用的是visual c++的环境编译;
我用的是dev c++和sublime txt 无法实现指定路径的写入。这个问题先留着
—————2020-1-1留;

10.3.3 用格式化的方式的读写文件

输入输出函数,如果是以终端为对象进行输入输出;(什么是终端,目前你理解为exe的执行窗口。)

printf();输出函数,从变量输出到终端;
scanf();输入函数,从终端输入到变量;

如果是以文件为对象,那么就不能使用以上的函数了;需要改为:

fprintf(文件指针,格式字符串,输出表列);
fprintf(fp,"%d,%6.2f",i,f);从变量输出到文件记事本中
fscanf(文件变量,格式字符串,输入表列)fscanf(fp,"%d,%f",&i,&f);从文件记事本中输入到变量中

文件是在磁盘中,变量是在内存里,他们之间只能通过二进制来转化。
输入时要将文件中的ascall码转换成二进制形式再保存在内存变量中,在输出的时候又要将内存中的二进制形式转换成字符。
这样实现了以文件为终端的输入输出,但是耗时比较多。

10.3.4用二进制向文件读写一组数据

那咱们不转化,直接针对二进制文件,对于某种二进制文件往往比较大,也就是说文件的数据比较多。就是将内存的二进制的数据块直接搬运到磁盘。将磁盘的二进制数据块直接搬运到内存中;这种以文件为对象的输入输出函数:

fread(buffer,size,count,fp);从文件读出,保存到内存。
 buffer:是一个地址。存放数据的(内存中的)存储区的地址
 size:要读写的字节数
 count:要读写多少个数据项(每个数据项长度为size)
 fp:文件的指针
fwrite(buffer,size,count,fp);从内存读出,写入文件
 buffer:是一个地址。将(内存中的)存储区的数据输出到文件,内存存储区地址。
 size:要读写的字节数
 count:要读写多少个数据项(每个数据项长度为size)
 fp:文件的指针
fread fwrite 函数执行成功 返回count的数值。是个整数。

咱们看一个例子,来看一看怎么使用这个函数:

/*从键盘输入10个学生的有关数据,然后把他们转存到磁盘文件上去。*/
#include <stdio.h>
#define SIZE 10
//定义一个结构体数组包含是个学生的数据
struct student_type
{
   char name[10];
   int num;
   int age;
   char addr[15];
}stud[SIZE];
//将10个同学的数据从内存中读取出来,然后就是输出到磁盘中
void save()
{
   FILE *fp;
   int i;
   //打开文件,这个文件在磁盘中
   if((fp=fopen("stu.dat","wb"))==NULL)//用“wb”写入文件中
   {
   	printf("cannot open file\n");
   	return ;
   }
   //输入到文件中
   for(i=0;i<SIZE;i++)
   	if(fwrite(&stud[i],sizeof(struct student_type),1,fp)!=1)
   		printf("file write error\n");
   	fclose(fp);
}
//主函数,将数据填入,并且执行
int main()
{
   int i;
   printf("please erter data of students:\n");
   for (int i = 0; i < SIZE; i++)
   {
   	scanf("%s%d%d%s",stud[i].name,&stud[i].num,&stud[i].age,stud[i].addr);
   	save();
   	return 0;
   }
}

这是从内存读出,写入文件的案例
那我同样把这段文件的二进制数据从文件中读出,写到内存中。

/*从键盘输入10个学生的有关数据,然后把他们转存到磁盘文件上去。*/
#include <stdio.h>
#include <stdlib.h>
#define SIZE 10
//定义结构体数组,包含十个数据
struct student_type
{
   char name[10];
   int num;
   int age;
   char addr[15];
}stud[SIZE];

定义指针变量,打开的文件指向指针变量,用fread 读写
int main()
{
   int i;
   FILE *fp;
   if((fp=fopen("stu.dat","rb"))==NULL)
   {
   	printf("cannot open file\n");
   	exit(0) ;
   }
   for(i=0;i<SIZE;i++)
   {
   	fread(&stud[i],sizeof(struct student_type),1,fp);
   	printf("%-10s %4d %4d %-15s",stud[i].name,stud[i].num,stud[i].age,stud[i].addr);
   }
   fclose(fp);
   return 0;
   }
}

注意:(??)
之前说了,在文件与内存之间。文件是在磁盘中,磁盘是在外存中。所以文件的数据换到内存中的时候,其中换行符和回车统统转换成换行符。就是说你代码上写一串换行符号,exe执行文件显示换行,你在exe文件敲打一个回车,同样是换行,就是说,数据从文件到内存,换行符和回车都被转换成换行。
从内存读出的时候,不发生字符转换。他也没有回车,只有换行符号。
咱们敲打的字写exe文件中的数据是在哪儿。送到内存的缓冲区。就相当于输入数据到内存。这是手动敲打。从文件读入也是输入数据的一种方式。都是一样的。都属于输送数据。
————————————————
总结
————————————————
就是输送数据:
1.exe上直接敲;手动输入数据
2.直接读文件数据;自动读入数据。
他们输入到内存都要转换。因为只有换行符号有对应的二进制代码。
————————————————————————————
这样理解可能是错的,读者这段独白属于在目前知识体系下的理解。仅供参考
——————————————————————————————

10.4随机读写数据文件

实际应用中,对于大批量的数据,如果想找到其中某一个人的数据。(假设整个人的数据在最后一个)按照顺寻读写,要读完前面所有数据,才能开始读写这个数据。造成的时间浪费是不可忍受的。
所以引入随机读写数据的概念。

10.4.1文件位置标记及其定位

a.文件位置标记就是:读写下一个字符的位置。
在这里插入图片描述当开始读写文件的时候,文件位置标记指向第一个位置。
当输入第一个文件的数据时。此时位置标记往后面顺推一个位置,指向第二个位置…
所谓文件标记位置就是下面那个你想要输入的字符输入的位置。
再看
顺序读写:文件位置标记按字节位置顺序移动的读写方式
随机读写:文件标记位置按需要移动到任意位置读写的方式

流式文件:既可以顺序读写,也可以随机读写
——————————————————————————————
b. 文件位置标记的定位

1)用rewind函数使文件位置标记指向文件开头。无返回值

2)用fseek函数改变文件标记的位置

fseek(文件指针类型,位移量,起始点);
文件指针:指向某个文件
位移量:以起始点为基准,向前移动的字节数,位移量是long型数据(末尾加一个L,就代表long型数据)
起始点:用012代替。
fseek(fp,100L,0);将文件标记向前移动到离文件开头100个字节处。
fseek(fp,50L,1);将文件标记向前移动到离当前位置50个字节处
fseek(fp,-10L,2);将文件标记位置从文件末尾处向后退10个字节

在这里插入图片描述3)用ftell函数测定文件的当前位置

从流式文件中得到标记的当前位置;
用相对于文件开头的位移量来表示;
调用出错返回-1L
i=ftell(fp);
if(i==-1L) printf("error\n");

10.4.2 随机读写(实例)

例子:有一个磁盘文件,内有一些信息。要求第一次将他的内容显示在屏幕上,第二次把他复制到另一个文件中上。


#include <stdio.h>
int main(int argc, char const *argv[])
{
   FILE *fp1 ,*fp2;
   fp1=fopen("file1.dat","r");
   fp2=fopen("file2.dat","w");
   while (!feof(fp1))
   	putchar(getc(fp1));
   putchar(10);
   rewind(fp1);
   while (!feof(fp1))
   	putc(getc(fp2));
   	fclose(fp1);
   	fclose(fp2);
   	return 0;
   }

例子:在磁盘文件中存有是个学生数据。要求将第1,3,5,7,9个数据输入计算机,并且在屏幕上显示出来。

#include <stdio.h>
#include <stdlib.h>
struct student_type
{
	char name[10];
	int num;
	int age;
	char addr[15];
 } stud[10];
 
 int main()
 {
 	int i;
	FILE *fp;
	if((fp=fopen("stu.dat","rb"))==NULL)
	{
		printf("can not open file\n");
		exit(0);
	 } 
	for(i=0;i<10;i+=2)
	{
		fseek(fp,i*sizeof(struct student_type),0);
		fread(&stud[i],sizeof(struct student_type),1,fp);
		printf("%-10s %4d %4d %-15s\n",stud[i].name,stud[i].num,stud[i].age,stud[i].addr);
	}
	fclose(fp);
	return 0;
 }

10.5 文件读写的出错检测

调用某一个函数如果正确,会返回一个数值。同理,如果错误,也会返回一个数值。所以对调用函数,可以通过返回值来判断函数的正确与否。
但是除了这个还可以通过特定 函数来检查。
1.ferror ----注意是针对 文件的输入输出函数

ferror(fp);
返回0:没有出错
返回非零:出错
fp:输入输出函数中所指向的文件地址。

通过对文件结果是否出现错误来判断函数是否执行。
当打开一个文件时候,这个ferror值自动调为0,以后每次对文件操作,ferror的数值都会进行改变。
2.clearerr
每次出现错误,ferror的数值都使一个非0的数值。此时要把这个数值调为0,才能使用ferror再次去判断。使文件错误标志还有文件结束标志调为0.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值