文件指针(c语言详细笔记)
如果你是寻找常用的缓冲文件系统函数,可直接跳到文章末尾.
一、数据流
数据流借用文件指针的移动来访问数据,文件指针目前所指的位置即是要处理的数据,经过访问后文件指针会自动向后移动。,每个数据文件后面都会有一个文件结束符号(EOF),用来告知该数据文件到此结束。若文件指针指到EOF时,则表示数据已访问结束。
二、文件
文件是指存放在外部存储介质(可以是磁盘、光盘、磁带等)上的数据集合、操作系统对外部介质上的数据是以文件形式进行管理的。
为标识一个文件,都必须有一个文件名作为访问文件的标志,其一般结构为 文件名.扩展名 。通常情况下一个包括盘符名、路径、主文件名和扩展名四部分信息。
程序在内存运行过程中与外存(外部存储介质)交互主要是通过以下两种方法:
1、以文件为单位将数据写到外存中;
2、从外存中根据文件名读取文件中的数据。
c语言支持的是流式文件,即前面说到的数据流,他把文件看作一个字节写,以字节为单位进行访问,没有记录的界限,即数据的输入和输出的开始和结束仅受程序控制,而不受物理符号(回车换行符)控制。
根据不同的角度对文件进行分类:
1、根据文件依附的介质:(1)普通文件是指驻留在磁盘或其他外部介质上的一个有序数据集。(2)设备文件是指与主机相连的各种外部设备。
2、根据文件的组织形式:(1)顺序读写文件,是指按从头到尾的顺序读出或写入的文件。(2)随机读写文件大都使用结构方式来存放数据,即每个记录的长度是相同的,因而通过计算便可直接访问文件中的特定记录。
3、根据文件存储形式:(1)ASCII码文件也称为文本文件,这种文件在磁盘中存放每个字符对应一个字节,用于存放对应的ASCII码。(2)二进制文件,是按二进制编码方式来存放文件的。
二进制文件和ASCII码文件的主要区别在于:
1、从存储形式上看,二进制文件是按该数据类型在内存中的存储形式存储的,而ASCII码文件则将该数据类型转换为可在屏幕上显示的形式来存储的。
2、从存储空间上来看,ASCII存储的方式所占空间比较多,而且所占空间大小与数值大小有关;
3、从读写时间上看,由于ASCII码文件在外存上以ASCII码存放,而在内存中的数据都是以二进制存放的。所以当进行文件读写时,要进行转换,造成存储速度较慢,对于二进制文件来说,数据就是按照在内存中的存储形式在内存上存放的。所以不需要进行这样的转换,在存取速度上较快。
4、从作用上看,由于ASCII文件可以通过编辑程序,进行建立和修改,也可以通过DOS中的type命令显示出来。因而ASCII文件通常哟怒存放输入数据及程序的最终结果,而二进制就不能显示出来所以用于暂存程序的中间结果,供另一段程序读取。
三、文件类型的指针
#include<stdio.h>
typedef struct{
// 缓冲区“满”或“空”的程度
short level;
// 文件状态标志
unsigned flags;
// 文件描述符
char fd;
// 如无缓冲区则不读取字符
unsigned char hold;
// 缓冲区的大小
short bsize;
// 数据缓冲区的读写位置
unsigned char *baffer;
// 指针指向当前文件的读写位置
unsigned char *curp;
// 临时文件,指示器
unsigned istemp;
// 用于有效性检查
short token;
}FILE;
FILE*fp;
四、打开文件
FILE *fopen(char *filename,char *mode);
使用mode模式打开指定- NNNNNNNNNNNN
B的filename文件,如打开文件成功返回有FILE类型的指针,如打开文件失败,则返回NULL;
第一个参数filename用来设定打开的文件,第二个参数用来设定打开的文件类型和指定文件的访问模式,必须是字符串格式,头尾必须用双引号括起来。
文件使用方式 | 意义 |
---|---|
“rt” | 只读打开一个文本文件 ,只允许读文件 |
“wt” | 只写打开或建立打开一个文本文件,只允许写数据 |
“at” | 追加打开一个文本文件,并在文件末尾写数据 |
“rb” | 只读打开或建立一个二进制文件,只允许读数据 |
“wb” | 只写打开或建立一个二进制文件,只允许写数据 |
“ab” | 追加打开一个二进制文件,并在文件末尾写数据 |
“rt+” | 读写打开一个文本文件 ,允许读和写 |
“wt+” | 读写打开或建立一个文本文件,允许读和写 |
“at+" | 追加打开一个文本文件,允许读,或在文件末尾写数据 |
“rb+” | 读写打开一个二进制文件 ,只允许读和写 |
“wb+” | 读写打开或建立一个二进制文件,允许读和写 |
“ab+" | 追加打开一个二进制文件,允许读,或在文件末尾写数据 |
//以只读方式打开一个可执行文件在相同路径下的文本文件test.txt
FILE *fp;
fp=fopen("test.text","r);
// 打开一个e盘下code文件夹下的exce文件data.xls
//嵌入式文件方法
//方法1
FILE *fp2;
fp2=fopen("e:\\code\\data.xls","r");
//方法2
FILE *fp3;
fp3=fopen("e:/code/data.xls","r");
方法一中指定路径时用了两个反斜线“\”,第一个表示转义字符,第二个表示根目录,方法二改用正斜线“/”也可以。
//交互式文件方法
FILE *fp;
char fiename[40];
gets(filename);
fp=fopen(filename,"r");
当打开一个文件时,需要通知给编译系统一下三个信息:
1、需要打开的文件名
2、使用文件的方式
3、让哪一个指针变量指向被打开的文件
对于文件的使用方式有一下几点说明:
1、文件使用方式由r,w,a,t,b,+,x等六个字符组成,各个字符的含义:
r(read) | 读 |
w(write) | 写 |
a(append) | 追加 |
t(text) | 文本文件,可省略不写 |
b(binary) | 二进制文件 |
x(xls) | excel文件,可省略不写 |
+ | 读和写 |
2、凡是用“r”打开一个文件时,该文件必须已经存在,且只能从该文件读出。
3.凡用“w”打开的文件只能像该文件写入,若打开的文件不存在,则以指定的文件名建立该文件,若打开的文件已经存在,则将该文件删去,重建一个文件夹。
4.若要向一个已存在的文件中追加新的信息,则只能用“a”方式打开文件,若此时此文件不存在,则会新建一个文件。
5.在打开一个文件时,如果出错,fopen将返回一个空指针NULL。在程序中可以用这一信息来判别是否完成打开文件的工作,并作相应的处理。
常用以下程序段打开文件:
//如果指针为空则表示不能打开e盘code文件夹下的test.txt文件,则给出信息
FILE *fp;
fp=fopen("e:\\code\\test.txt","r");
if(fp==NULL){
printf("\n不能打开e:\\code\\test.txt file!");
getchar();
exit(1);
}
6.标准输入文件(键盘),标准输出文件(显示器),标准出错输出(出错信息)是由系统打开的,可直接使用。
五、关闭文件
int fclose(FILE *fp);
将文件指针fp所指的文件关闭,若返回0,则表示关闭成功,若返回非0值则表示有错误发生。
fclose(fp);
在程序中,一个文件使用完毕后,若采用读取模式打开文件,可以不必做关闭文件的操作;若采用写入模式,一定要使用fclose()关闭文件,否则最后放在缓冲区的数据无法写会文件,从而发生数据遗失的情况。这是因为当向文件写数据时,是先将数据写到缓冲区,带缓冲区充满后再整块传送到磁盘上,如果程序结束时,缓冲区尚未充满,则其中的数据并没有传到磁盘上,必须使用fclose()函数关闭文件,强制系统将缓冲区的所有数据传送到磁盘上,并释放该文件指针变量;否则这些数据可能知识被送到了缓冲区中,而并没有真正写入到磁盘文件中。
由系统打开的标准设备文件,系统会自行关闭。
六、获取文件属性
int fileno(FILE *fp);
返回所打开文件指针fp对应文件描述之(handle_no)。当打开文件成功后,操作系统会自动赋予一个号码,此号码用来代表所打开的文件,所在头文件是stdlib.h
long filelength(int handle_no);
返回文件描述字(handle_no)对应的文件大小,以字节为单位。所在头文件为io.h
FILE *fp;
int fno,fsize;
fp=fopen("e:\\code\\test.txt","rt");
fno=fileno(fp);
fsize=filelength(fno);
fclose(fp);
// 采用交互式文件方式打开指定文件,若文件打开成功,则显示该文件的大小(byte);若文件打开失败,则提示出错信息。
#include<stdio.h>
#include<stdlib.h>
#include<io.h>
#define LEN 100
void main(){
FILE *fp;
char filename[LEN];
int fno,fsize;
printf("请输入要打开文件的完整路径及文件名:");
gets(filename);
fp=fopen(filename,"r");
if(fp==NULL){
printf("\n打开文件失败,%s可能不存在\n",filename);
exit(1);
}
fno=fileno(filename);
fsize=filelength(fno);
printf("\n%s文件打开成功,文件大小%dBytes\n",filename,fsize);
fclose(fp);
}
七、文件顺序读写
文件的读写方式分为顺序读写和随机读写。
顺序读写时指,将文件从头到尾逐个数据读出或写入。文件的读写时通过读函数实现的,
单字符读写函数分别是:fgetc和fputc。
字符串读写函数分别是:fgets和fputs。
格式化读写函数分别是:fscanf和fprintf。
数据块读写函数分别是:fread和fwrite。
1、单字符读写函数
int fgetc(FILE *fp);
读取文件中fp目前所指文件位置中的字符,读取完毕,文件指针自动往下移一个字符位置,若文件指针已经到文件结尾,则返回-1;
cg=fgetc(fp);
其意义是从fp所指的文件中读取一个字符并送入ch中。
对于fgetc函数的使用有以下几点说明:
(1).在fgetc函数调用中,读取的文件必须是以读或读写方式打开的;
(2).读取字符的结果也可以不给字符变量赋值。
(3).在文件内部有一个位置指针,用来指向文件的当前读写位置。在文件打开时,该指针总是指向文件的第一个字节。使用fgetc函数后,该位置指针将自动向后移动一个字节。因此可连续多次使用fgetc函数,读取多个字符。应注意文件指针和文件内部的位置指针不是一回事。文件指针是指向整个文件的,需在程序中定义说明,只要不重新赋值,文件指针的值是不变的。文件内部的位置指针用以指示文件内部的当前读写位置,每读写一次,该指针自动向后移动,它不需在程序中定义说明,而是有系统自动设置的。
#include<stdio.h>
#include<stdlib.h>
#include<io.h>
#define LEN 100
int main(){
FILE *fp;
char filename[LEN];
int fno,fsize;
char ch;
printf("请输入要打开文件的完整路径及文件名:");
gets(filename);
fp=fopen(filename,"rt");
if(fp==NULL){
printf("\n打开文件失败,%s可能不存在\n",filename);
exit(1);
}
fno=(fileno(fp));
fsize=filelength(fno);
printf("\n%s文件打开!\n",filename);
printf("\n文件大小%dBytes\n",fsize);
printf("\n文件内容为:");
while((ch=fgetc(fp))!=EOF)
printf("%c",ch);
fclose(fp);
printf("\n\n");
return 0;
}
关于符号常量EOF:
(1)、在对ASCII码文件进行读入操作时,如果遇到文件为,则读操作函数返回一个文件结束标志EOF,其值在头文件stdio.h已被定义值为-1;
(2)、在对二进制文件进入读入操作时,必须使用库函数feof()来判断是否遇到文件尾。
2、写单字符函数fputc()
int fputc(char ch,FILE *fp);
把字符ch写入文件指针fp所指向文件的位置,成功时返回字符的ASCII码,失败时返回EOF(在stdio.h中,符号常量EOF的值等于-1)。
FILE *fp;fputc('a',fp);
其意义是把字符a写入fp的指向文件中。
fputc函数的使用说明:
(1)被写入的字符可以用写、读写、追加方式可打开。用写或读写方式打开一个已存在的文件时,将清除原有的文件内容,写入字符从文件首开始。如需保留原有文件内容,希望写入的字符从文件末开始存放,必须以追加方式打开文件,被写入的文件若不存在,则创建该文件。
(2)每写入一个字符,文件内部位置指针向后移动一个字节。
(3)fputc函数有一个返回值,如写入成功则返回写入的字符,否则返回一个EOF。可以此来判断写入是否成功。
#include<stdio.h>
#include<stdlib.h>
#include<io.h>
#include<ctype.h>
#include<string.h>
#define LEN 100
int main(){
FILE *fp;
char filename[LEN],data[LEN];
int fno,fsize,i;
char ch;
printf("写文件程序...\n");
printf("请输入要打开文件完整路径及文件名:");
gets(filename);
fp=fopen(filename,"a+");
if(fp==NULL){
printf("\n打开文件失败,%s可能不存在\n",filename);
exit(1);
}
fno=fileno(fp);
fsize=filelength(fno);
printf("\n%s文件打开!\n",filename);
printf("\n文件大小%dBytes\n",fsize);
printf("\n文件内容为:");
while((ch=fgetc(fp))!=EOF)
printf("%c",ch);
// 这里添加数据
while(1){
printf("\n\n请问是否要添加数据(Y/N):");
if(toupper(getchar())=='Y'){
printf("\n\n请输入要添加的数据:");
getchar();
gets(data);
for(i=0;i<strlen(data);i++){
fputc(data[i],fp);
}
}
else{
fclose(fp);
break;
}
}
// 检测是否写入了数据
fp=fopen(filename,"r");
if(fp==NULL){
printf("\n写入数据失败!\n");
exit(1);
}
while((ch=fgetc(fp))!=EOF)
printf("%c",ch);
fclose(fp);
return 0;
}
3、字符串读写函数
(1)读字符串函数fgets()
char *fgets(char *str,FILE *fp);
FILE *fp;
char str[10];
fp=fopen("e:\\code\\test.txt","rt");
while(fgets(str,10,fp)!=NULL)
printf("%s",str);
其意义是从fp所指的文件中读取10个字符送入数组str中,接着再将str字符串打印出。若文件读不到数据则会返回NULL,此时会离开循环。
(2)写字符串函数fputs()
int fputs(char *str,FILE *fp);
FILE *fp;
char str[10];
fp=fopen("e:\\code\\test.txt","rt");
gets(str);
fputs(str,fp);
其意义是把字符串str中的内容写入文件指针fp所指的文件中。
4、格式化字符串读写函数
(1)格式化字符串读写函数fscanf()
int fscanf(FILE *fp,"格式化字符串",输入项地址表);
从文件指针fp所指向的文件中按照格式字符串指定的格式,将文件中的数据送到输入项地址中。若读取的数据成功则返回所读取数据的个数,并将数据按照指定格式存入内存中的变量和数组中,文件指针向下移动;若读取失败则返回EOF;
char num[20],name[40].sex[5];
FILE *fp;
fp=fopen("e:\\code\\test.txt","rt");
fscanf(fp,"%s %s %s",nnum,name,sex);
(2)格式化读写函数fprintf()
int fprintf("e:\\code\\test.txt","rt");
char num[20]="0402009",name[40]="王瑶晴",sex[5]="女";
FILE*fp;
fp=fopen("e:\\code\\test.txt","a+");
fputc('\n',fp);
fprintf(fp,"%s %s %s",num,name,sex);
注意:如果输入的字符串不带空格,那么读和写都全部正确,但是当输入的字符串中带有空格时,即使向文件中写入的数据依然保持正确,在读取数据时也会发生错误。这是因为fscanf()函数读取数据时以空格作为数据与数据之间的间隔,要解决输入含有空格数据的问题,就必须使用下面要介绍的随机文件读写函数fread()和fwrite();
5、数据块读写操作
(1)数据块读函数fread()
int fread(void *buffer,int size,int count,FILE *fp);
从文件指针fp所指当前位置开始,一次读入size个字节,重复count此,并将数据存到buffer开始的内存区中,同时将读写位置指针后移size*count次,该函数的返回值是实际读取的count值。
各个参数的含义如下:
buffer
是一个指针,在fread函数中,它表示存放读入数据的首地址(即存放在何处);在fwrite函数中,它表示要输出的数据在内存中的首地址(要从何处开始存储)。
size
它表示数据块的字节数。
count
表示要读写的数据块块数。
fp
表示文件指针。
float fa[5];
fread(fa,4,5,fp);
表示从fp所指的文件中每次读4个字节,连续读5次,即读五个实数到fa中。
(2)数据块写函数fwrite()
int fwrite(void *buffer,int size,int count,FILE *fp);
从buffer所指向的内存区开始,一次输出size个字节,重复count次,并将输出的数据存入到fp所指的文件中,同时将读写位置指针后移size*count次。
float fa[5];
fwrite(fa,4,5,fp);
例如一个结构体类型数据:
struct student_type{
char name[10};
int num;
int age;
char addr[30];
)stu[40];
//写入文件(前提是stu数组中40个元素都已经有值存在
fwrite(&stu[i],sizeof(struct student_type),40,fp);
//从磁盘文件中读出(前提是fp所指向的文件中也有值存在):
fread(stu,sizeof(struct student_type),40,fp);
注意:fscanf和fprintf是成对出现的,即函数fprintf()写出的文件,要使用函数fscanf()来读入;同理函数fread和fwrite也必须成对出现。
六、文件的随机读写
1、函数rewind()
void rewind(FILE *fp);
将文件内部的位置指针移到文件开始位置
2、函数fseek()
int fseel(FILE *fp,long offsert,int whence);
文件指针有whence地址移到offsert的地址
rewind(fp)
fseek(fp,sizeof(struct stu)*n,0);
fread(&student,sizeof(struct stu),1,fp);
输出第n个位置的数据。
3、函数ftell()
函数ftell()的作用是得到流式文件的当前位置,用相对于文件开头的位移量来表示。如果ftell函数返回值是-1L,则表示出错。
i=ftell(fp);
if(i==-1L) printf("Error\n");
4、其他读写函数
大多数c编译系统的提供另外两个函数putw()和getw(),用来对磁盘文件读一些字(整数)。
//将10输出到fp所指向的文件
putw(10,fp);
// 从磁盘文件中读一个整数到内存,赋给整数变量i
i=getw(fp);
如果c编译的库函数中不包括putw和getw函数,可以自己定义这两个函数。
putw(int i,FILE *fp){
char *s;
s=&i;
putc(s[0],fp);
putc(s[i],fp);
return i;
}
getw(FILE *fp){
char *s;
int i;
s=&i;
s[0]=getc(fp);
s[1]=getc(fp);
return i;
}
七、出错检查
1、函数feof()
int feof(FILE *fp);
判断文件指针fp是否处于文件结束位置,若文件结束,则返回1,否则返回0;
2、函数ferror()
int ferror(FILER *fp);
检查文件在用各种输入/输出函数进行读写时是否出错,如果ferror返回值为0表示未出错,否则表示出错。
注意:在同一个文件每一次调用输入/输出函数,均产生一个新的ferror函数值,因此,应当执行fopen函数时,ferror函数的初始值自动置于0;
3、函数clearerr()
int clearerr(FILE *fp);
用于清楚出错标志和文件结束标志,使他们为0。