| §12.1 概述 | §12.2 文件类型指针 |
| §12.3 文件的打开与关闭 | §12.4 文件的读写 |
| §12.5 文件的定位 | §12.6 出错的检测 |
| §12.7 非缓冲文件系统 | 本章要求 |
一、文件的概念
1、“文件(file)”:是记录在介质上的数据的集合,以文件名作为访问文件的标识。
介质:一般是磁盘、磁带、光盘等。
2、内存文件:在内存中开辟一段空间,以文件的方式存放数据。如、DOS虚拟磁盘驱动程序RAMDRIVE.SYS建立的虚拟磁盘上存放的文件。Windows9.X恢复盘也建立虚拟磁盘,用于临时存放系统命令文件。
3、操作系统把与主机关联的终端也当作文件处理,如:
键盘: 输入文件
显示器:输出文件
打印机:输出文件
4、C语言把文件看作一个字节序列,即由一连串的字节组成,称为“流(stream)” ,以字节为单位访问,没有记录的界限(实际存在界限,例如,在FAT16文件系统中,文件的最大长度是2GB)。
5、按文件所依附的介质来分:有磁盘文件、磁带文件、内存文件、设备文件等。
6、按文件的内容区分:有源程序文件、目标文件、数据文件等。
7、按文件中的数据组织形式来分:数据文件可分为ASCII码文件和二进制文件。
ASCII码文件,又称为“文本文件”(text),其每一个字节存放一个ASCII码。
二进制文件,把内存中的数据按其在内存中的存储形式存放在磁盘上。
例、十进制整数10000,
在内存中占两字节,其存放形式是:0010,0111,0001,0000。
在二进制文件中也按这中方式存放。
在ASCII文件中,存放为31H、30H、30H、30H、30H,占五个字节,它们分别是1、0、0、0、0、0字母的ASCII码。
8、按照操作系统对磁盘文件的读写方式,文件可以分为“缓冲文件系统”和“非缓冲文件系统”。
缓冲文件系统:操作系统在内存中为每一个正在使用的文件开辟一个读写缓冲区。

非缓冲文件系统:操作系统不开辟读写缓冲区。
要调用一个文件,需要有以下的信息:
文件当前的读写位置
与该文件对应的内存缓冲区的地址
缓冲区中未被处理的字符串
文件操作方式等
缓冲文件系统会为每一个文件系统开辟一个“文件信息区”,在stdio.h中,它被定义为FILE类型数据。
typedef struct
{
int _fd; /* 文件号 */
int _cleft; /* 缓冲区中剩下的字节数
*/
int _mode; /* 文件操作模式 */
char * _nextc; /* 下一个字节的位置
*/
char * _buff; /*文件缓冲区位置 */
}FILE;
其中:每个成员就是用来存放有关文件的各种信息的数据项。
定义文件指针变量的一般形式为:
FILE *文件结构指针变量名
例如、FILE *fp;
注意:只有通过文件指针,才能调用相应的文件。
文件操作的过程:对磁盘文件的操作必须“先打开,后读写,最后关闭”。
“打开”文件的含义:以某中方式从磁盘上查找指定的文件或创建一个新文件。
FILE * fopen(const char *filename,const char *mode);
filename: 文件名(可以包含逻辑驱动器、路径、文件名、扩展名)
mode: 打开方式
FILE *:返回值
如:
FILE *fp;
fp = fopen("file1","r");
如果成功打开,返回一个指向被打开文件的文件信息区的起始地址;
如果打开失败,返回一个NULL指针。
| 文件打开方式 | 含义 | ||||||
| |||||||
| |||||||
| |||||||
| |||||||
说明:
1、打开文件的方式,必须与文件的属性兼容。例如,不能以"w"和"a"方式打开具有“只读”属性的文件。但可以以任何方式打开“隐藏”属性文件。
2、DOS/Windows中,无“只写”文件属性,一个文件可写,同时意味着可读。
3、用带"r"的方式("r"、"rb"、"r+"、"rb+")打开文件时,若文件不存在,则返回NULL指针。一般用以下方式检查是否成功打开:
FILE *fp;
if ((fp=fopen("file1", "r")) = =NULL
)
{
printf("cannot open this file\n");
exit(0);
}
exit(0)是程序返回操作系统。
4、在Turbo C2.0中,用带"a"或带"w"的方式打开文件时,若文件不存在,则创建该文件。但fopen()函数仍可能因为磁盘写保护或目录满而失败。
5、在Turbo C2.0中,用带"a"或带"w"的方式打开文件时,若文件已存在,"a"方式在原文件后追加数据,"w"方式把原文件删除、并重新创建一个相同名字的文件。
文件的关闭:“关闭”文件的作用:
(1)使文件指针fp与文件脱离。
(2)刷新文件输入/输出缓冲区。
关闭方法:
fclose(fp);
一、fputc()/fgetc()
int fputc(int c, FILE *fp)----把字符c写入文件fp,成功时返回字符c的ASCII码,失败时返回EOF(在stdio.h中,符号常量EOF的值等于-1)。
int fgetc(FILE
*fp)----从文件fp中读一个字符,返回读得的字符。
对于文本文件,遇文件尾时返回EOF。
对于二进制文件,用feof(fp)
判别是否遇文件尾。feof(fp)=1说明遇文件尾。
例、从文本文件test中顺序读入文件内容,并在屏幕上显示出来。
#include "stdio.h"
void main()
{
FILE *fp;
char ch;
fp = fopen("test",
"r");
if (fp == NULL)
{
printf("can not open test\n");
exit(0);
}
ch = fgetc(fp);
while(ch != EOF)
{
putchar(ch);
ch = fgetc(fp);
}
fclose(fp);
}
例、从二进制文件test中读入文件内容.
#include "stdio.h"
void main()
{
FILE *fp;
char ch;
fp = fopen("test", "rb");
if (fp == NULL)
{
printf("can not open
test\n");
exit(0);
}
while(!feof(fp))
{
ch = fgetc(fp);
....
}
fclose(fp);
}
[例12.1] 从键盘输入一些字符,逐个把它们送入磁盘文件,知道从键盘输入#为止。
#include "stdio.h"
void main()
{
FILE *fp;
char ch;
char filename[10]; /*
文件名 */
printf("Input filename\n");
scanf("%s\n",
filename); /* 从键盘输入文件名 */
if ((fp = fopen(filename, "w"))==NULL) /*
打开文件 */
{
printf("can not open file %s\n", filename);
exit(0);
}
ch = getchar();
while(ch != '#') /* 从键盘读入字符,直到#为止 */
{
fputc(ch,fp);
putchar(ch);
ch = getchar();
}
fclose(fp); /* 关闭文件 */
}
[例12.2]、将一个磁盘文件的内容复制到另一个磁盘文件。
#include "stdio.h"
void main()
{
FILE *in, *out;
char ch;
char infile[10], outfile[10];
printf("Enter the infile name\n");
scanf("%s", infile);
printf("Enter the outfile name\n");
scanf("%s", outfile);
if ((in = fopen(infile, "r"))==NULL)
{ printf("can not open infile
%s\n", infile);
exit(0);
}
if ((out = fopen(outfile,
"w"))==NULL)
{ printf("can not open outfile %s\n", outfile);
exit(0);
}
while(!feof(in)) fputc(fgetc(in), out);
fclose(in);
fclose(out);
}
二、fread()/fwrite()
size_t fread(void *buffer, size_t size, size_t count, FILE *fp);
从文件fp中读入count次,每次读size字节,读入的信息存在buffer指针指向的缓冲区。函数返回值等于实际读入的次数(可能少于count)。
size_t fwrite(void *buffer, size_t size, size_t count, FILE *fp);
将buffer地址开始的信息,写入count次,每次写size字节至文件fp中。函数返回值等于实际写入的次数(可能少于count)。
[例]、结构体类型数据。
struct student_type
{
char name[10];
int num;
int
age;
char addr[30];
}stu[40];
写入文件:
for(i=0; i<40; i++) /* 每次写一个学生 */
fwrite(&stu[i], sizeof(struct
student_type), 1, fp);
或:写一次
fwrite(stu, sizeof(struct student_type), 40, fp);
从磁盘文件读出:
for(i=0; i<40; i++)
fread(&stu[i], sizeof(struct student_type),
1, fp);
或:
fread(&stu[i], sizeof(struct student_type), 40, fp);
[例12.3] 从键盘上输入一批学生数据,然后存储到磁盘上。
#include "stdio.h"
#define SIZE 4
struct student_type
{
char name[10];
int num;
int
age;
char addr[15];
}stud[SIZE];
void save(); /* 原型 */
void load(); /* 原型 */
void main()
{ int i;
for(i=0; iSIZE; i++) /* 从键盘读入数据
*/
scanf("%s%d%d%s",
stud[i].name, &stud[i].num,
&stud[i].age, stud[i].addr);
save(); /* 存盘 */
load(); /* 从盘读出 */
for(i=0; iSIZE; i++) /* 屏幕上显示 */
printf("%-10s%4d%4d%-15s\n",
stud[i].name, stud[i].num, stud[i].age, stud[i].addr);
}
void save()
{ FILE *fp;
int i;
if ((fp=fopen("stu_list", "wb"))==NULL)
{
printf("can not open
file\n";
exit(0);
}
for(i=0; iSIZE; i++)
if (fwrite(&stud[i], sizeof(struct
student_type), 1, fp) != 1)
printf("file read error\n");
}
void load()
{ FILE *fp;
int i;
if ((fp=fopen("stu_list", "rb"))==NULL)
{
printf("can not open
file\n";
return;
}
for(i=0; iSIZE; i++)
if (fread(&stud[i], sizeof(struct
student_type), 1, fp) != 1)
{
if (feof(fp)) return;
printf("file read error\n");
}
}
三、fprintf()/fsacnf()函数
fprintf(文件指针,格式控制,变量列表);
fscanf (文件指针,格式控制,变量地址列表);
除增加“文件指针”外,与printf()/scanf()用法相同。
四、其它读写磁盘函数
int putw(int w, FILE *fp) /* 整数w写入文件fp */
int getw(FILE *fp) /*
从文件fp读一个整数 */
char * fgets(char *str, int n, FILE
*fp);
从文件fp读n-1个字节到str,str最后一个字节加'\0'。
int fputs(const char *str, FILE
*fp)
把str写入fp。
文件指针FILE
*fp中,包含一个读写位置指针char *_nextc,它指向下一次文件读写的位置。
typedef struct
{
int _fd; /* 文件号 */
int _cleft;
/* 缓冲区中剩下的字节数 */
int _mode; /* 文件操作模式 */
char * _nextc; /* 下一个字节的位置 */
char *
_buff; /*文件缓冲区位置 */
}FILE;
每当进行一次读写后,该指针自动指向下一次读写的位置。
当文件刚打开或创建时,该指针指向文件的开始位置。
可以用函数ftell()获得当前的位置指针,也可以用rewind()/fseek()函数改变位置指针,使其指向需要读写的位置。
一、rewind()函数
原型:void rewind(FILE *fp)
作用:使文件fp的位置指针指向文件开始。
[例12.4] 把一个文件的内容显示在屏幕上,并同时复制到另一个文件。
#include "stdio.h"
void main()
{
FILE *fp1, *fp2;
fp1 = fopen("file1.c", "r"); /* 源文件
*/
fp2 = fopen("file2.c", "w"); /* 复制到file2.c */
while(!feof(fp1)) putchar(fgetc(fp1)); /* 显示到屏幕上 */
rewind(fp1); /* fp回到开始位置 */
while(!feof(fp1)) fputc(fgetc(fp1), fp2);
fclose(fp1);
fclose(fp2);
}
二、fseek()函数
原型:int fssek (FILE *fp, long offset, int
origin);
功能:把文件fp的位置指针从起始点origin开始,移动offset字节。
成功返回0,失败返回非0。
| 起始点origin | 符号常量 | 值 |
| 文件开始位置 | SEEK_SET | 0 |
| 当前位置 | SEEK_CUR | 1 |
| 文件尾 | SEEK_END | 2 |
[例12.5] 在磁盘文件stud_dat上,存有10个学生(0~9)的数据,读出1、3、5、7、9号学生数据,并在屏幕上显示出来。
#include "stdio.h"
struct student_type
{
char name[10];
int num;
int
age;
char sex;
}stud[10];
void main()
{
int i;
FILE * fp;
if ((fp=fopen("stud_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), SEEK_SET);
fread(&stu[i], sizeof(struct student_type), 1, fp);
printf("%s %d %d %c\n",
stud[i].name, stud[i].num, stud[i].age,
stud[i].sex);
}
fclose(fp);
}
三、ftell()函数
原型:long ftell (FILE *fp);
功能: 获得文件fp的当前位置指针。若返回-1则失败。
在文件操作时,如果出错,除了操作函数的返回值有所反应外(如fopen()函数返回NULL),还可以用ferror()函数获得是否出错。
原型:int ferror(FILE *fp)
功能:若上一次文件操作未出错,返回0;否则返回非0。
文件操作出现错误后,该错误信息将一直保留在系统中,ferror()函数可以取走该错误信息(同时清除),也可以用clearerr()函数清除。
原型:void clearerr(FILE *fp)
功能:清除一次文件操作的错误。
缓冲文件系统用文件指针FILE *fp代表一个文件。
非缓冲文件系统用一个整数来代表一个文件,该整数称为“文件代号”或“文件句柄”。非缓冲文件操作的函数原型在io.h中声明。
1、open()
原型:int open(const char * filename, int
mode);
功能:以mode方式打开文件filename,返回文件代号。
失败时返回-1。
打开方式:
(符号常量在fcntl.h中定义)
O_RDONLY----只读
O_WRONLY----只写
O_RDWR
-----读写
O_CREAT-----创建
O_TEXT------文本文件
O_BINARY----二进制文件
例、int fd
fd = open("test.h", O_RDWR | O_CREAT | O_BINARY);
if (fd
== -1) { printf("can not open file\n"); exit(0); }
2、close()
原型: int close(int fd);
功能:关闭文件fd。成功时返回0,失败时返回-1。
3、creat()
原型:int creat(const char *filename, int mode);
功能:创建文件。成功返回文件代号。
4、read()
原型:int read(int fd, void *buf, unsigned
count)
功能:从文件fd中取count字节到buf缓冲区,返回实际读得的字节数。
5、write()
原型:int write(int fd, void *buf, unsigned
count)
功能:把buf缓冲区的count字节内容写入文件fd中,返回实际写入的字节数。
6、lseek()
原型:long lseek(int fd, long offset, int
origin)
功能:把fd文件的读写位置指针从起始点origin开始移动offset字节。返回实际移动的字节数。失败时返回-1L。
1、文件中数据的组织形式:文本文件、二进制文件。
2、 打开文件的含义,为什么要关闭文件?
3、缓冲文件操作函数的使用。