§10.1 C文件概述
文件(file)是程序设计中的一个重要的概念。所谓"文件"一般指:存储在外部介质上的数据的集合。一批数据是以文件的形式存放在外部介质(如磁盘)上的。操作系统是以文件为单位对数据进行管理的,也就是说,如果想找存在外部介质上的数据,必须先按文件名找到所指定的文件,然后再从该文件中读取数据。要向外部介质上存储数据也必须先建立一个文件(以文件名标识),才能向它输出数据。
§10.2 文件类型指针
在缓冲文件系统中,关键的概念是"文件指针"。每个被使用的文件都在内存中开辟一个区域,用来存放文件的有关信息(如文件名字、文件的状态以及文件的当前位置等)。这些信息是保存在一个结构体类型的变量中的。该结构体类型是由系统定义的,取名为FILE。有的C版本在stdio.h文件中有以下类型的定义:
typedef struct
{
int -fd; /* 文件号 */
int -cleft; /* 缓冲区中剩下的字符 */
int -mode; /* 文件的操作模式 */
char *-nextc; /* 下一个字符的位置 */
char *-buff; /* 文件的缓冲区的位置 */
}FILE;
有了FILE类型之后,可以用它来定义若干个FILE类型的变量,以便存放若干个文件的信息。例如,可以定义以下FILE类型的数组。
FILE -efile[-MAXFILE];
定义了一个结构体数组-efile[ ],它有-MAXFILE个元素,-MAXFILE是符号常量,它的值是可使用的文件的最大数目。
可以定义文件类型的指针变量。如:
FILE *fp;
fp是一个指向FILE类型结构体的指针变量。可以使fp指向某一个文件的结构体变量,从而通过该结构体中的文件信息能够访问该文件。
§10.3 文件的打开和关闭
和其它高级语言,对文件读写之前应该"打开"文件,在使用结束之后应"关闭"该文件。
10.3.1 文件的打开(fopen函数)
ANSI C 规定了标准输入输出函数库,用fopen()函数来实现打开文件。fopen()函数的调用方式通常为:
FILE *fp;
fp=fopen(文件名, 使用文件的方式);
例如:
fp=("d:\aa.txt", "r");
它表示:要打开D盘根目录下aa.txt文件来读数据。fopen()函数带回指向aa.txt文件的指针并赋给fp,这样fp就和aa.txt相联系了,或者说,fp指向aa.txt文件了。可以看出,在打开一个文件时,通知给编译系统以下三个信息:1.需要打开的文件名,也就是准备访问的文件的名字。2.使用文件的方式(读还是写等)。3.让哪一个指针变量指向被打开的文件。
使用文件的方式见表10.1。
表10.1
文件使用方式 | 含 义 |
"r" (只读) | 为输入打开一个文本文件 |
"w" (只写) | 为输出打开一个文本文件 |
"a" (追加) | 向文本文件尾增加数据 |
"rb" (只读) | 为输入打开一个二进制文件 |
"wb” (只写) | 为输出打开一个二进制文件 |
"ab" (追加) | 向二进制文件尾增加数据 |
"r+" (读写) | 为读/写打开一个文本文件 |
"w+" (读写) | 为读/写建立一个新的文本文件 |
"a+" (读写) | 为读/写打开一个文本文件 |
"rb+" (读写) | 为读/写打开一个二进制文件 |
"wb+" (读写) | 为读/写建立一个新的二进制文件 |
"ab+" (读写) | 为读/写打开一个二进制文件 |
说明:
1. 用"r"方式打开的文件只能用于向计算机输入而不能用于向该文件输出数据,而且该文件应该已经存在,不能打开一个并不存在的用于"r"方式的文件(即输入文件),否则出错。
2. 用"w"方式打开的文件只能用于向该文件写数据,而不能用来向计算机输入。如果原来不存在的文件,则在打开时新建一个以指定名字命名的文件。如果原来已经存在一个以该文件名命名的,则在打开时将该文件删除,重新建立一个新文件。
3. 如果希望向文件末尾添加新数据,用"a"方式打开,但此时文件必须存在,否则将出现出错信息。打开时位置指针移到文件末尾。
4. 用"r+"方式时该文件已经存在,以便能向计算机输入数据。 用"w+"方式则建立一个文件,先向文件写数据,然后可以读此文件中的数据。
5. 如果不能实现"打开"的任务,fopen函数将会带回一个出错信息,此时fopen函数将会带回一个空指针值NULL。常用下面的方法打开一个文件:
if ((fp = fopen("file1", " r "))= =NULL)
{ printf ("cannot open this filen ");
exit(0);
} 即先检查是否出错,如果有错就在终端上输出"cannot open this file"。exit函数的作用是关闭所有文件,终止正调用过程。待程序员检查出错误,修改后再运行。
6. 用以上方式可以打开文本文件或二进制文件,用同一种缓冲文件系统来处理文本文件和二进制文件。
7. 在用文本文件向计算机输入时,将回车换行符转换为一个换行符,在输出时把换行符转换为回车和换行两个字符。在用二进制文件时,不进行这种转换。
8. 在程序开始运行时,系统自动打开三个标准文件:标准输入、标准输出、标准出错输出。通常这三个文件都与终端相联系。系统自动定义了三个 文件指针stdin、stdout和stderr分别指向终端输入、终端输出、标准出错输出。如果在程序中指定要从stdin所指的文件输入数据,就是指从终端键盘输入数据。
10.3.1 文件的关闭(fclose函数)
在使用完一个文件后应该关闭它,以防止它再被误用。"关闭"就是使文件指针变量不指向该文件,此后不能再通过该指针对其相连的文件进行读写操作,除非再次打开,使该指针变量重新指向该文件。
用fclose函数关闭文件。fclose函数调用的一般形式为: fclose(文件指针);
例如:fclose(fp); 用fclose函数打开文件时所带回的指针赋给了fp,现把该文件关闭。应养成在程序终止上前关闭所有使用的文件的习惯,如果不关闭文件将会丢失数据。fclose函数返回一个值,顺利执行关闭操作返回值为0;如果返回非0值,则表示关闭时有错误。
§10.4 文件的读写
10.4.1 fputc函数和fgetc函数
一、 fputc函数把一个字符写到磁盘文件中去。其一般形式为 fputc(ch,fp);
其中ch是要输出的字符,它可以是一个字符常量也可以是一个字符变量。fp是文件指针变量,它从fopen 函数得到返回值。 fputc函数的作用是将字符(ch的值)输出到fp所指向的文件上去。如果输出成功fputc函数的返回值就是输出的字符;如果输出失败,则返回一个EOF。
二、fgetc函数
从指定文件读入一个字符,该文件必须是以读或读写方式打开的。fgetc函数的调用形式为:
ch = fgetc(fp);
fp为文件型指针变量,ch为字符变量。fgetc函数带回一个字符赋给ch。如果在执行fgetc读字符时遇到文件结束符,函数返回一个文件结束标志EOF。如果想从一个磁盘文件顺序读入字符并在屏幕上显示出来可以:
ch = fgetc(fp);
while(ch! = EOF)
{
putcher( ch );
ch = fgetc(fp);
}
注意:EOF不是可输出字符,因此不能在屏幕上显示。如果想顺序读入一个二进制文件中的数据,可以用: while( !feof (fp) )
{ c = fgetc(fp);
┇
} 当未遇文件结束,feof(fp)的值为0。!feof(fp)的值为1,这种方法也适用于文本文件。
三、fputc函数和fgetc函数使用举例
例:从键盘输入一些字符,逐个把它们送到键盘上去,直到输入一个"#"为止。
#include <stdio.h >
void main(void)
{FILE *fp;
char ch,filename[10];
scanf("%s ",filename);
if ((fp = fopen ( filename, "w ")) = =NULL)
{
printf("cannot open filen ");
exit(0);
}
ch = getchar( );
while (ch!= ’#’)
{
fputc(ch,fp);
putchar(ch);
ch=getchar( );
}
fclose(fp);
}
运行情况如下:file1.c (输入磁盘文件名)
computer and c# (输入一个字符串)
computer and c (输入一个字符串)
本例运行时,从键盘输入磁盘文件名"file1.c",然后输入要写入该磁盘文件的字符"computer and c",同时在屏幕上显示这些字符。
12.4.2 fread函数和fwrite函数
用getc和putc函数可以用来读写文件中的一个字符。但是常常要求一次读入 一组数据, fread函数和fwrite函数可以用来读写一个数据块。它们一般调用形式为:
fread (buffer,size,count,fp);
fwrite(buffer,size,count,fp);
buffer:是一个指针。对fread它是读入数据的存放地址。对fwrite是输出数据的地址。
size:要读写的字节数。
count:要进行读写多少个size字节的数据项。
fp:文件型指针。
如果文件以二进制形式打开,用fread和fwrite函数就可以读写任何类型的信息,如 fread(f,4,2,fp); 其中f是一个实型数组名。一个实型变量占4个字节。这个函数从fp所指向的文件读入2次数据,存储到数组f中。如果有如下一个结构体类型:
sturt student_type
{ char name[10];
int num;
int age;
char addr[30];
} stud[40]; 结构体数组stud共40个元素,每一个元素用来存放一个学生数据,假设学生的数据已存放在磁盘文件中,可以用下面的语句读入40个学生数据。
for(i = 0; i<40;i++)
fread(&stud[i],sizeof(struct student_type),1,fp);
同样,以下的语句可以将内存中学生数据输出到磁盘文件中。
for (i=0;i<40;i++)
fread(&stud[i],sizeof(struct student_type),1,fp);
下面写出一个完整的程序。
例:从键盘输入4个学生的有关数据,然后把它们转存到磁盘文件中去。
# include <stdio.h>
#define SIZE 4
struct student_type
{ char name[10];
int num;
int age;
char addr[15];
} stud[SIZE];
void save( void)
{ FILE *fp;
int i;
if ((fp=fopen("stu_list","wb"))= =NULL)
{
print("cannot open filen");
return;
}
for(i=0;i<SIZE;i++)
if(fwrite(&stud[i],sizeof(struct student_type),1,fp)!=1)
printf("file write errorn");
}
void main(void)
{ int i;
for (i=0;i<SIZE;i++)
scanf("%s%d%d%s",stud[i].name,&stud[i].num,&stud[i].age, stud[i].addr);
save( );
}
10.4.3 fprintf函数和fscanf函数
fprintf函数和fscanf函数与 printf函数、scanf 函数相仿,都是格式化读写函数。只有一点不同:fprintf函数和fscanf函数的读写对象不是终端而是磁盘文件。一般调用方式为:
fprintf(文件指针,格式字符串,输出表列);
fscanf(文件指针,格式字符串,输入表列);
例如: fprintf(fp,"%d,%6.2f",i,t);它的作用是将整型变量i和实型变量t的值按%d和%6.2f的格式输出到fp指向的文件上。如果 i=3,t=4.5,则输出到磁盘文件上的是以下的字符串:
3,4.50
同样,用以下fscanf 函数可以从磁盘文件上读入ASCⅡ字符:fscanf(fp,"%d,%f",&i,&t);
这样将磁盘文件中的数据送给变量i,t。
10.4.4 其它读写函数
一、putw函数和getw函数
这两个函数用来对磁盘文件读写一个字。例如 putw(10,fp)的作用是将整数10输出到fp 指向的文件。而i=getw(fp); 的作用是从磁盘文件读一个整数到内存,赋给整型变量i。
二、fgets函数和fputs函数
fgets函数的作用是从指定文件读入一个字符串。如:fgets(str,n,fp);
从fp指向的文件输入n-1个字符,并把它们放到字符数组str中。如果在读入 n-1个字符结束之前遇到换行符或EOF,读入即结束。字符串读入后在最后加一个’