目录
一、文件的随机读写
二、便准输入输出重定向
一、文件的随机读写
前面的例程执行的都是顺序文件处理(Sequential File Processing)。在顺序文件处理过程中,数据项是一个接着一个进行读取或者写入的。例如,如果想读取文件中的第5个数据项,那么使用顺序存取方法必须先读取前4个数据项才能读取第5个数据项。不同于顺序读写的是,文件的随机访问(Random Access)允许在文件中的随机定位,并在文件的任何位置直接读写数据。
为了实现文件的定位,在每一个打开的文件中,都有一个文件位置指针(File Location Pointer),也称文件位置标记,用来指向当前读写文件的位置,它保存了文件中的位置信息。当对文件进行顺序读写时,每读完一个字节后,该位置指针自动移到下一个字节的位置。当需要随机读写文件数据时,则需要强制移动文件位置指针指向特定的位置。那么如何定位文件的位置指针呢?C语言提供了如下两个函数来定文件位置指针。
int fseek(FILE *fp , long offset, int fromwhere);
void rewind(FILE *fp);
其中,函数fseek()的功能是将fp的文件位置指针从fromwhere开始移动offset个字节,指示下一个要读取的数据的位置。
offset是一个偏移量,它告诉文件位置指针要跳过多少个字节。offset为正时,向后移动,为负时,向前移动。ANSI C要求位移量offset是长整型数据(常量数据后要加L)。这样当文件的长度大于64kb是不至于出问题。
fromwhere用于确定偏移量计算的起始位置,它的可能取值有3中:
1、SEEK_SET或0,代表文件开始处;
2、SEEK_CUR或1,代表文件当前位置;
3、SEEK_END或2,代表文件结尾处。
通过指定fromwhere和offset的值,可使位置指针移动到文件的任意位置,从而实现文件的随机读取。如果函数fseek()调用成功,则返回0值,否则返回非0值。
函数rewind()的功能是将文件位置指针指向文件首字节,即重置位置指针到文件首部。
C语言还提供了一个用来读取当前文件位置指针的函数,其函数原型为:
long ftell (FILE *fp);
若函数调用成功,则返回文件的当前读写位置,否则返回-1L。函数ftell()用相对于文件起始位置的字节偏移量来表示返回的当前文件位置指针。
注意:缓冲区为文件传输读写提高了速度,但是也有一些副作用。
例如,在缓冲区内容还未写入磁盘时,计算机突然死机或掉电,数据就会丢失,永远也找不回来。再如,缓冲区被写入无用的数据时,如果不清除,其后的文件读操作都首先会读取那些无用的数据。那么如何解决这个问题呢?
为了解决这个问题,C语言提供了如下的函数:
int fflush (FILE *fp);
fflush()的功能是无条件地把缓冲区中地所有数据写入物理设备。这样,程序员可自己决定再何时清除缓冲区中的数据,以确保输出缓冲区中地内容写入文件。
例题:编程从文件student.txt中随机读取第k条记录地数据并显示到屏幕上,k由用户从键盘输入。
#include <stdio.h>
#include <stdlib.h>
typedef struct date
{
int year;
int month;
int day;
}DATE;
typedef struct student
{
long studentid;
char studentname[10];
char studentsex;
DATE birthday;
int score[4];
float aver;
}STUDENT;
void searchinfile(char filename[],long k);
int main(void)
{
long k;
printf("Input the searching record number:");
scanf("%ld",&k);
searchinfile("student.txt",k);
return 0;
}
void searchinfile(char filename[],long k)
{
FILE *fp;
int j;
STUDENT stu;
if((fp = fopen(filename,"r"))==NULL)
{
printf("Failure to open %s!\n",filename);
exit(0);
}
fseek(fp,(k-1)*sizeof(STUDENT),SEEK_SET);
fread(&stu,sizeof(STUDENT),1,fp);
printf("%10ld%8s%3c%6d/%02d/%02d",stu.studentid,stu.studentname,stu.studentsex,stu.birthday.year,stu.birthday.month,stu.birthday.day);
for(j=0;j<4;j++)
{
printf("%4d",stu.score[j]);
}
printf("%6.lf\n",stu.aver);
fclose(fp);
}
程序第28~49行定义的函数searchinfile()的功能是从文件filename中查找并显示第k条记录的数据,函数serchinfile()的第1个形参filename是字符数组,用于存放需要读取的文件名,程序第24行将文件名字符串“student.txt”作为实参传递给形参filename,函数searchinfile()的第2个形参k是长整型变量,表示要读取的记录号,为了在文件中直接读取第k条记录,程序第38行用函数fseek()将文件位置指针从文件开头向后移动(k-1)*sizeof(STUDENT)个字节。
关于偏移量,这里之所以这样计算偏移量,是因为fseek()的第2个参数要给出文件位置指针所需跳过的字节数,二每条记录的长度是sizeof(STUDENT)个字节。
同理,因函数ftell()返回的文件位置使用字节偏移量表示的,所以必须通过除以sizeof(STUDENT)才能换算成当前的记录号,例如,若在本例题中:
printf ("record number = %d\n",ftell(fp)/sizeof(STUDENT)+1);
这说明在执行第38行语句后文件位置指针指向了第2条记录,而用函数fread()读取一条记录数据后,文件位置指针又指向了吓一条记录,即第3条记录。
二、便准输入输出重定向
前面程序对数据的输入/输出是通过终端设备来完成的,看似与文件毫无瓜葛,但实际上,对于终端设备,系统自动会打开3个标准文件:标准输入、标准输出和标准错误输出。
相应的,系统定义了3个特别的文件指针常数:stdin、stdout、stderr,分别指向便准输入、输出和错误文件,这3个文件都以标准终端设备作为输入/输出对象。在默认情况下,标准输入设备是键盘,输出设备是屏幕。
1.fprintf是printf的文件操作版,二者的差别就在于fprintf多一个FILE *类型的参数fp。
2.fputc和putchar差别跟fprintf和printf的差别差不多。
例如:
putchar(c); fputc(c,stdout);
3.getchar(); == fgetc(stdin);
4.puts(str); == fputs(str,stdout);
5.fgets()比gets()多一个参数size。
char *fgets(char *s,int size,FILE *stream);
char *gets(char *s);
例如:
gets(buffer);
fgets(buffer, sizeof(buffer),stdin);
虽然系统隐含的标准I/O文件是指终端设备,但其实标准输入和标准输出是可以重新定向的,操作系统可以重定向它们到其他文件或具有文件属性的设备,只有标准错误输出不能重定向。(简单描述:没有显示器屏幕,重定向后可以通过打印机输出)
这里,用“<”表示输入重定向,用“>”表示输出重定向。
例如:假设exefile是可执行程序的文件名,指向该程序时,需要输入数据,现在如果要求从文件file.in中读取数据,而非键盘输入,那么DOS命令提示符下,只要键入如下命令即可:
C:\exefile.exe<file.in
于是,exefile的便准输入就被重定向到了file.in,此时程序exefile只会专心致志地从文件file.in中读数据,而不再理会你此后按下的任何一个按键。
freopen()可用于指定模式将输入或输出重定向到另一个文件。
常见地使用方式:
freopen("D:\\in.txt","r",stdin);//将输入流定位到in.txt
freopen("CON","r",stdin); //将输入流还原到键盘
freopen("D:\\out.txt","w",stdout);//将输出流定位到out.txt
freopen("CON","w",stdout);//将输出流还原到屏幕