1、含义:
存储在外存储器上,相较于常量、变量、数据等都是存储在内存储器上的。
键盘是输入文件stdin;(只读)
显示器是输出文件stdout;(只写)
写文件,我们理解为输出,从内存到外存;比如printf,fprintf,fputc,fputs,fwrite;
读文件,我们理解为输入,从外存到内存;比如scanf,fscanf,fgetc,fgets,fread;
2、分类:
1)按照存取方式:
顺序存取:读写操作总是从文件的开始位置开始;
随机存取:读写操作可以从文件的任意位置进行。
2)按照存取形式:
文本文件:每个字符都是以ASCII码值的形式存储;定长译码编码,但文件可识别。
二进制文件:把数据按其在内存中的存储形式(二进制码)形式存储;不定长译码编码,但文件不可识别。
例如:
3.14,文本文件,会将每个数字看做是字符存储的,一个字符占一个字节;//打开文件后,所见即所得;
若作为二进制文件存储的话,如果使用float类型,则是占4个字节,使用double则是占8个字节。//打开文件后,是乱码;
但如果文本文件和二进制文件都存储的是字符数组的内容的话,打开二进制文件,看到的是对应的十六进制数。
3、文件指针
#include <stdio.h>
typedef struct{
short level; //缓冲区满或空 程度
unsigned flags;//文件状态标志
char fd; //文件描述符
unsigned char hold;// 如无缓冲区不读取字符
char filename[20];//文件名
char path[30];//文件路径
}FILE;
FILE* fp;
4、打开文件
对文件进行读写操作前,需要打开文件:
注意:在linux系统下,mode中加b与不加b是一样的。例如,r和rb是一样的。以下是在windows平台下的区别,在linux下,我们如果添加b,只是为了以后方便移植到windows平台。
FILE* fp = fopen(文件名,文件的打开方式);
例如:
FILE* fp = fopen("test.txt","r");
FILE* fp = fopen("\\nand_flash\\test.txt","r");\\用两个/表示一个\
若文件无法打开,会返回NULL。
故:
FILE* fp;
if((fp = fopen("test.txt","r"))==NULL)
{
printf("fopen file fail\n");
exit;
}
打开方式 | 含义 | 打开方式 | 含义 |
“r” | 只读,打开文本文件,文件必须存在 | “r+” | 可读写,打开文本文件,文件必须存在 |
“w” | 只写,打开文本文件,文件不必存在,若存在,fopen的时候内容会被清除 | “w+” | 可读写,建立并打开新的文本文件,文件可以不存在,如果存在,fopen的时候内容会被清除 |
“a” | 追加写,文件可以不存在,从文本文件尾开始添加 | “a+” | 可读写,打开文本文件,文件可以不存在,在文件末尾添加 |
"rb" | 只读,打开二进制文件,文件必须存在 | "rb+" | 读写,打开二进制文件,文件必须存在 |
“wb” | 只写,打开二进制文件,文件不必存在,若存在,内容会被清除 | “wb+” | 读写,建立并打开新的二进制文件,文件可以不存在,如果存在,内容会被清除 |
"ab" | 追加,文件可以不存在,从二进制文件尾开始添加 | "ab+" | 读写,打开二进制文件,文件可以不存在,在文件末尾添加 |
r与r+比较:二者都需要先建立文件;r+增加了可写的功能,即:打开一次文件,可以写文件,读文件;写的时候是在文件位置指针的位置写的,会覆盖指针后面的内容。写几个字节覆盖几个字节。
r+的写优势:1)文件位置指针在第一个数据前,不会清除原文件的所有内容,通过fseek()函数来改变文件位置指针来写内容(写几个,覆盖几个)。2)读写文件时,只需要打开一次文件即可。
w与w+比较:二者对文件的存在都没有要求,如果文件存在,fopen的时候,原文件被删除,重新创立了新的文件;w+增加了读功能,即:写入文件后,文件位置指针在最后一个数据后,通过fseek来更改文件位置指针,还可以将文件的内容读出来,
注意,w+是无法先读后写,因为,使用fopen后,就是一个空的文件。
r与w的比较:r文件必须要存在;w文件可存在,可不存在,如果存在,原文件中的内容会被覆盖(fopen的时候,原文件被删除,重新生成了一个同名的文件),如果不存在,会建立这个文件。
w的优势,重新生成一个新的文件,并且会覆盖原文件。
r+与w+的比较:二者都既能读又能写;r+要求文件存在,w+不要求文件存在,若存在,则会删除重新创建新的文件(即使用w+属性,无论文件存不存在,都会重新创建);
r+可以先写后读,也可以先读后写(因为文件已存在,且其中有内容),w+只能先写后读,因为fopen之后,文件为新建的文件为空,故只能先写后读。
w与a的比较:二者都不要求文件是否存在;w无论文件存在与否,都会删除文件,新建文件;a的话,如果文件未创建的话,与w一样,如果文件创建的话,则会在文件末尾续写内容。
a与a+的比较:二者都不要求文件是否存在,都不能改变文件的原内容;a只能在末尾添加,文件位置指针无效(即fseek无效)即:只能在文件末尾写,不能读;
a+只能在结尾追加写, 文件位置指针只对读有效, (追加写操作会将文件指针移动到文件尾),即只要追加写操作会将文件位置指针挪动到最后一个数据后,而无论前面是否使用了fseek。
但是当第一次读文件时,文件指针是在第一个数据前的。写操作执行后,指针就在最后一个数据之后了。(可以这么理解,文件打开后,默认的位置指针都是在第一个字符前,但是如果此时要写入数据的话,会同时跟着一个fseek(fp,0L,SEEK_END);操作,所以,只要是追加写之后,如果要读字符的话,就必须首先挪动文件位置指针。
小结:打开文件后的第一次读操作都是从文件第一个数据前开始的,无论是r,r+,a+(w+第一个是空文件,没法读取);
w、w+、肯定是在起始位置写入;a、a+只能在末尾写。
5、关闭文件
对文件读写完成后,应该关闭,防止丢失数据;
关闭文件的实质,是释放文件指针,使文件指针变量不再指向该文件。
fclose(fp);
//关闭成功返回0;
//关闭失败返回非0
6、文件的读写
6.1 fscanf 和fprintf //读写文本文件,数据格式任意
记忆:fscanf与fprintf 与scanf 和printf的格式及含义基本上是相同的;
scanf,是从键盘(stdin文件)读取一个值,并赋值给一个变量;//fscanf也是从文件中读取值,赋给某个变量
scanf("%d",&m)等价于 fscanf(stdin,"%d",&m);
printf,是将一个变量的值,写入到显示器(stdout文件);//fprintf也是将某个变量写到文件中;
printf("%d",n);等价于 fprintf(stdout,"%d",n);
1)fprintf(文件指针,格式字符串,输出项表)
例如:
fprintf(fp,"%d",m);//将变量m以%d的形式写到fp指向的文件中
2)fscanf(文件指针,格式字符串,输入项表)
例如:
fscanf(fp,"%d",&n);//注意取地址符号&//将fp所指的文件以%d的形式读取到变量n中
6.2 fputc(putc)和fgetc(getc)//从文本文件中读写字符
fputc(ch,fp);//ch可以是字符常量,可以是字符变量,写入到fp所指的文件中
putchar(ch);等价于fputc(ch,stdout);
ch = fgetc(fp);//从fp所指的文件中读取字符作为返回值赋给字符型变量ch
ch = getchar();等价于ch = getc(stdin);
6.3 fputs和fgets函数// 从文本文件中读写字符串
fputs(str,fp);//str是待写入文件的字符串的首地址
//注意:字符串最后的\0不会写入文件,也不会自动加'\n';
写入成功,函数值为非0,否则为0;
fgets(str,n,fp);//str是存放字符串的起始地址;n是int型变量
//从fp所指的文件中读n-1个字符依次放入到以str为旗帜地址的内存中,读入结束后,自动在最后加'\n',并以str作为函数值返回。
//注意:调用该函数时,最多只能读取n-1个字符。要为'\0'保留一个字符的空间
6.4 fwrite和fread//二进制文件中读写数据
fwrite(buffer,size,count,fp);
//buffer为指针,代表内存中一段存储空间的首地址;
size,代表每单元的字节数;
count代表要进行读写的单元数
//将以buffer为其实地址的内存中的count个单元,每个单元size个字节写到fp所指的文件中。
int a[4]={1,2,3,4}; fwrite(a,2,4,fp);
//将以a为起始地址的内存中的4个单元,每单元2个字节写(输出)到fp所指的文件中。
fread(buffer,size,count,fp);
//buffer是指针,代表内存中存储空间的地址;
count代表进行读写的单元数;
size带包每单元的字节数;
//从fp所指的文件中读(输入)count个单元,每个单元size个字节,到以buffer为起始地址的内存中。
int a[4];
fread(a,2,4,fp);
//从fp所指的we年中读4个单元,每个单元2个字节,到以a为起始地址的内存中。
经过测试,当使用文本形式打开文件的时候,写入文件的内容均为字符串,此时使用fwrite、fread函数也能 正常读写文件。可以参考本文。
fopne("1.dat","w");
char temp[1024];
sprintf(temp,"mm=%d;\r\n",mm);
fwrite(temp,strlen(temp),1,fp);
7、判断文件是否结束
1)feof(fp); 判断文件是否结束------ f end of file//可以判断二进制文件,也可以判断文本文件
文件结束:返回值为1;
文件没有结束,返回值为0;
2)EOF;//文本文件
因为文本文件都是ASCII码,0-255,不可能为-1,所以使用EOF作为文本文件的结束标志。
注意:fgets()函数读到'/n'就停止,而不管是否达到数目要求。同时在读取的字符串的最后加上'/0'。
fgets()函数执行完以后,返回一个指向该串的指针。如果读到文件尾或出错,则均返回一个空指针NULL,所以常用feof()函数来测定是否到了文件尾或者是ferror()函数来测试是否出错。
#include "stdio.h"
int main() {
FILE *fp;
char str[128];
if((fp=fopen("test.txt", "r"))==NULL) {
printf("cannot open file/n"); exit(1);
}
while(!feof(fp)) {
if(fgets(str, 128, fp)!=NULL)
printf("%s", str);
}
fclose(fp);
}
char s[1024];
while((fgets(s,1024,fp))!=NULL)
{
printf("%s",s);
}
8、文件定位函数
文件fopen后,文件位置指针指向文件的开头,第一个数据之前;//注意是文件位置指针,不是文件指针
文件fclose后,文件位置指针指向文件末尾,最后一个数据之后。
对数据进行读操作后,文件位置指针指向尚未读数据之前,即是从指针后的数据开始读。
当数据进行写操作后,文件位置真值指向刚写入数据之后。
我们使用fseek函数实现改变文件的位置指针。
起始点 | 名称 | 代表数字 |
文件开始位置 | SEEK_SET | 0 |
文件当前位置 | SEEK_CUR | 1 |
文件末尾位置 | SEEK_END | 2 |
默认的就是文件开始位置,所以,SEEK_SET表示的是离文件开始位置的字节数,set 即off_set
fseek(fp,offset,origin);//一般用于二进制文件
offset是以origin为基点,以字节为单位的位移量,当offset为正整数,表示位置指针从指定位置向后移动,当offset为负整数,表示位置指针从指定位置向前移动,为长整型。
fseek(fp,30L,SEEK_SET);//位置指针从开始位置向后移30个字节。
fseek(fp,-10*sizeof(int),SEEK_END);//位置指针从文件尾部位置向前移10*sizeof(int)个字节。
fseek(fp,0L,SEEK_SET);//位置指针移动到开始位置。
fseek(fp,0L,SEEK_END);//位置指针移动到末尾位置。
//ftell告诉你离开始的地方有多远了。。。//也可以说是当前的位置是哪?
long t = ftell(fp);
//返回当前位置指针据文件开头的字节数
//该函数可以用来获取文件的大小
fseek(fp,0L,SEEK_END);
long t = ftell(fp);
void rewind(fp);//无返回值
//使文件位置指针返回到文件开头位置,等价于 fseek(fp,0L,SEEK_SET);
9、fflush()函数
int fflush(FILE* stream);
//如果参数为NULL,会刷新所有打开的输出的流
fflush(NULL);
比如:printf("hello world"); 有时候会打印不出来。
标准输出是行缓冲模式,只有遇到换行符或者一行满了之后,才会刷新缓冲区,所以,我们会添加'\n'------》printf("hello world\n"); 或者使用printf("hello world"); fflush(stdout);
缓冲区的作用:合并系统调用;
模式:
- 行缓冲---->换行的时候刷新或满了时候刷新或强制刷新(fflush):如stdout
- 全缓冲---->满了时候刷新或强制刷新(fflush)--->这是默认的方式,只要不是终端设备的都是全缓冲。
- 无缓冲---->需要立即输出的内容,如stderr.
可以通过setvbuf修改缓冲模式的相关内容(不建议修改)
10、总结
1)c语言中关于文件的操作都是以f开头的。
2)r+,w+,a+,其实都可以通过r,w,a及fseek函数多次操作实现。