18:58:49 2020-10-06
文件操作(流,函数[参数,返回值,功能])
流:
三个基本的流,stdin、stdout、stdout 这个三个是已经在stdio.h文件中弄好的文件指针,也是标准流,分别是标准输入流(键盘)、标准输出流(显示屏)、标准错误流(显示器)
基本操作(函数)
1、打开文件
FILE *fopen(const char *filename,const char *mode);
功能:打开指定的文件
参数:第一个参数是文件名,第二个参数是打开的模式
打开模式两大类:文本模式和二进制模式
返回值:返回一个文件指针
文本模式:
“r”:只读,没有创建的义务
“w”:只写,并且是将指定的文件内容清零后,才将内容写入其中;有创建的义务
“a”:只写,并且是在文本末尾追加的方式进行写;有创建的义务
“r+”:可读可写;没有创建的义务
“w+”:可读可写;有创建的义务
“a+”:可读可写,并且是文本末尾追加的方式进行写;有创建的义务
二进制模式:
“rb” ”wb” “ab” “ab+” “a+b” “wb+” “w+b” “ab+”
2、关闭文件
int fclose(FILE *stream);
功能:关闭文件指针所指向的文件
参数:只有一个参数,要关闭的文件的指针
返回值:关闭成功返回0,否则返回eof
3、文件顺序读取数据
int fscanf(FILE *stream,const char *format,…)
功能:从文件指针所指的文件中读取数据,读取的格式由后面的参数来决定,并传递给相关格式的变量中
参数:第一个参数为需要读取文件的文件指针,后面的参数是用来决定读取数据的格式是什么,以及相关格式的变量地址。
返回值:读取到的数据的个数。
比如:fscanf(fp,”%s%d%d”,name,&chinese,&math);它的返回值为3
4、向指定的文件写入数据
int fprintf(FILE *stream,const char *format,….)
功能:将指定的数据输出到文件指针所指的文件中去
参数:第一个参数目标文件的文件指针,用于接收内存中的数据,后面的参数是用来决定输出到文件的数据的格式是什么,以及输出的相关变量。
返回值:输出的字节数
比如:
c=fprintf(stderr,"can't open put FILE\n");
printf("c=%d\n",c);
此时的c=20刚好为输出的字符串中字符个数+1个结束符’\0’
文本模式——操作函数
5、从文本中读取一个字符到内存中
int fgetc(FILE *stream)
功能:从文本中读取一个字符到内存中
参数:只有一个参数,需要读取的文件指针
返回值:从文本中读取的字符的ascii值
测试代码
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#define slen 20
int main(){
FILE *out;
FILE *in;
char s[slen];
char t[slen];
char n;
char *p;
char c;
p=t;
printf("filename:");
gets(s);
printf("输入的文本为:");
gets(t);
if((in=fopen(s,"w"))==null){
fprintf(stderr,"can't open %s FILE\n",s);
exit(1);
}
while(*p!='\0'){
n=fputc(*p,in);
p++;
printf("n=%c\n",n);
}
fclose(in);
if((out=fopen(s,"r"))==null){
fprintf(stderr,"can't open %s file\n",s);
exit(2);
}
c=fgetc(out);
if(c!=eof)
fprintf(stdout,"%c",c);
fclose(out);
return 0;
}
6、从内存中输出一个字符到指定的文本中
int fputc(char ch,FILE *stream)
功能:将指定的字符ch,输出到文件指针所指向的文件中
参数:第一个参数为要输出到文本的字符,第二个参数为文件指针
返回值:第一个参数(写入文本字符的ascii码值)
7、从文件中获得字符串
char * fgets(const char *buff,int max,FILE *fp)
功能:从指定的文件中最多读取max-1个字符,存储到内存的buff区域。
读到什么时候停止?有三种情况
case1:遇到第一个换行符时,读完换行符后,停止。(没错,它读了换行符,和fscanf函数不一样)
case2:读取到字符个数为max-1时停止
case3:遇到文件结束符(换句话来说就是读取到文件结尾),然后fgets()函数会向末尾添加一个空字符以构成一个字符串。所以字符串的最大长度代表字符的最大数目再加上一个空字符。
对于case2:
如果fgets()函数在达到字符最大数目之前读完了一整行,它将在字符串的空字符之前添加一个换行符以标识一行结束。这是fgets()函数和gets()函数的不同之处,后者读取换行符后将其丢弃。
参数:第一个参数为char 数组的名称,第二个参数是max为字符串的最大长度,第三个参数为FILE指针
返回值:fgets()遇到eof的时候会返回null值,可以据此检查文件结尾。否则,它返回传递给它的地址值。
8、从内存中输出字符串到文件中去
int fputs(const char *buff,FILE *fp)
功能:将内存中的buff字符串,写入fp指针所指向的文件中
参数:第一个参数为字符串,第二个参数为文件指针
返回值:成功输出到文本中返回非负数;反之返回eof。
注意:fputs()函数不会添加换行符;而fgets()函数保留了换行符,所以这两个最佳搭档,配合默契。
二进制模式——操作函数
思考:由于是二进制的形式,那么就只有0和1这两个数字组成,在写入或者读取的时候,就需要指定一次读取或者写入的字节数,总共读取或者写入多少次。还要指定写入或者读取的地址。
1、二进制模式将内存中的内容写入到文件的操作
int fwrite(const void *ptr , size_t size , size_t count , FILE *stream)
功能:以二进制的形式将内存中的内容写入到磁盘中。
参数:第一个参数ptr是要写入的数据块的地址;第二个参数size表示要写入的数据块的大小(以字节为单位);第三个参数count表示总共到写入到文件的数据块的数目;第四个参数为指定文件的文件指针。
返回值:返回成功写入的项目数(数据块个数)。正常情况下,它与count相等,但是如果有写入错误的话,返回值就会小于count.
这里有一个点需要注意:在fwrite()原型中有一个奇怪的声明:void *ptr。fwrite()的一个问题就是它的第一个参数不是一个固定类型。可以是字符指针,可以是double指针,也可以是其他类型。
例如:要保存一个256字节大小的数据对象(如一个数组),可以这样做:
char buffer[256];
fwrite(buffer,256,1,fp);
这一调用将一块256字节大小的数据块从缓冲区写入到文件。
例如:要保存一个包含10个double值的数组,可以这样做:
double earning[10];
fwrite(earning,sizeof(double),10,fp);
这一调用将earnings数组中的数据写入文件,数据块分成10块,每块都是double大小。
2、将二进制文件读到内存中的操作
Size_t fread(void *ptr, size_t size, size_t count , FILE *fp);
fread()函数与fwrite()函数的参数相同
功能:将文件中的内容以二进制的形式读取到内存中
参数:第一个参数为将读取的内容存储到内存的数据块地址;第二个参数size表示读取的数据块大小(以字节为单位);第三个参数count表示总共读取到内存中的数据块总数。第四个参数为指定文件的文件指针。
返回值:fread()函数返回成功读入的项目数。正常情况下,它与count相等,但是如果有读取错误的话,返回值就会小于count。
其他文件操作函数(不涉及读写操作)
1、将文件指针放到文件中指定位置的操作函数
int fseek(FILE *stream, long offset, int fromwhere);
功能:这个函数可以把文件当做数组,定位到你指定的位置,用于随机读取。
参数:第一个参数是一个指向被搜索文件的FILE指针。(注意:在此之前应该已经使用fopen()打开了该文件了。);第二个参数是偏移量,表示从起始点开始要移动的距离,这个参数必须是long类型的值,可以为正(前移动,即起始点的右侧)、负(后移动,即起始点的左侧),也可以为零(保持不动)。第三个参数是规定文件的起始点模式,有三种起始点模式。
模式 |
偏移量的起始点 |
SEEK_SET |
文件开始 |
SEEK_CUR |
当前位置 |
SEEK_END |
文件结尾 |
返回值:如果一切正常,fseek()返回值为0.如果有错误出现,例如试图移动超出文件范围,则fseek()的返回值为-1。
为啥是long型?设计这个函数的程序员规定了呗,偏移范围是long能表示的范围。也就是说,这个函数只能随机访问文件大小在long范围之内的文件。在fseek()函数中,定义的是有符号的long型,范围为:-2147483648~2147483647(-2^31~(2^31-1)),超过这个范围,会出错。有人就遇到这个情况,比如:https://bbs.csdn.net/topics/240082964
例如:
fseek(fp,0L,SEEK_SET);//找到文件的开始处
fseek(fp,10L,SEEK_SET);//找到文件的第10个字节
fseek(fp,2L,SEEK_CUR);//从文件的当前位置向前移动2个字节
fseek(fp,0L,SEEK_END);//到达文件结尾处
fseek(fp,-10L,SEEK_END);//文件结尾处退回10个字节
2、告诉文件指针当前所在的位置,参考系是文件开始处。
long int ftell(FILE *stream);
功能:告诉文件指针当前的位置。
参数:只有一个参数为文件指针。
返回值:返回文件的当前位置,如果发生错误,则返回-1L。
ftell()函数通过返回距文件开始处的字节数目来确定文件的位置,文件的第一个字节到文件开始处的距离是字节0,依此类推。这种定义,适用于以二进制模式打开的文件,对于以文本模式打开的文件来讲,不一定是这样。
fseek()函数与ftell()函数的配合如下:
首先
fseek(fp,0L,SEEK_END);
把当前位置设置为从文件结尾处偏移0字节处,也就是将位置设定在文件结尾。
接下来的语句:
last=ftell(fp);
把从文件开始到文件结尾的字节数目赋给last。接下来是一个循环:
for(count=1L;count<=last;count++){
fseek(fp,-count,SEEK_END);/*回退*/
ch=getc(fp);
}
将文件开始到文件起初定义的内容反向输出。
前面提到fseek()和ftell()函数的一个潜在问题是它们限制文件的大小只能在long类型的表示范围之内。因此接下来引入两个用来处理较大文件的新的定位函数。这两个函数不是采用long类型值,而是使用一种称为fpos_t(代表FILE position type,文件定位类型)的新类型来代表位置。fpos_t不是一种基本类型,而是通过其他类型定义的。一个fpos_t类型的变量或者数据对象可以用来指定文件中的一个位置,它不能是一种数组类型,但除此之外不再有其他限制。因此C实现可以提供一种满足特殊平台需求的类型;例如:这种类型可以作为结构来实现。
3、获取文件当前位置(与ftell()函数功能相同)
int fgetpos(FILE *stream, fpos_t *pos)
功能:该函数在pos所指的位置放置一个fpos_t值;这个值描述了文件中的一个位置。
参数:第一个参数为文件指针;第二个参数为指向fpos_t对象的指针。
返回值:如果成功,函数返回0;否则返回一个非零值。
4、设定文件指针的位置(与fseek()函数功能相同)
int fsetpos(FILE *stream, const fpos_t *pos);
功能:使用pos指向的位置上的那个fpos_t值来设定文件指针指向该值所指示的位置。
参数:第一个参数为文件指针;第二个参数为指向fpos_t对象的指针。
返回值:如果成功,函数返回0;否则返回一个非零值。
注意:fsetpos()函数中的fpos_t值应该是通过调用fgetpos()函数获取的!
6、将指定的字符返回到输入流中去。
int ungetc(int c,FILE *fp);
功能:该函数将c指定的字符放回输入流中。
参数:第一个参数为将要放到输入流的字符;第二个参数为输入流(文件指针)。
返回值:如果成功,则返回被推入的字符;否则返回EOF,且文件指针的位置保持不动。
如果输入流中放入了一个字符,下一次调用标准输入函数就会读入那个字符。
7、建立一个供标准I/O函数使用的替换缓冲区。(打开文件以后,在没有对流进行任何操作以前,可以调用这个函数)通俗来说,就是自己指定一个空间来作为缓冲区供标准I/0使用。
setbuf()和setvbuf()函数的实际意义在于:用户打开一个文件后,可以建立自己的文件缓冲区,而不必使用fopen()函数打开文件时设定的默认缓冲区。这样就可以让用户自己来控制缓冲区,包括改变缓冲区大小、定时刷新缓冲区、改变缓冲区类型、删除流中默认的缓冲区、为不带缓冲区的流开辟缓冲区等。
int setvbuf(FILE *fp, char *buffer, int mode, size_t size)
功能:建立一个供标准I/O函数使用的替换缓冲区。
参数:第一个参数为一个打开的标准流,指针fp来指定流;第二个参数buffer指向将使用的存储区。如果buffer的值不是NULL,则就必须创建这个缓冲区,例如:声明一个1024个字符的数组来作为缓冲区;如果buffer的值是NULL,则函数会自动为自己分配一个缓冲区。第三个参数指定文件缓冲的模式,有三种:
模式 |
描述 |
_IOFBF |
全缓冲:一次性写入或者一次性输出(缓冲区满时刷新) |
_IOLBF |
行缓冲:一行一行写入或者输出(缓冲区满的时候或者一个新行写入的时候刷新) |
_IONBF |
没有缓冲 |
第四个参数为setvbuf()函数指定数组的大小(size_t类型是一种派生整数类型)
返回值:如果成功,则该函数返回0,否则返回非零值。
8、对缓冲区刷新
fflush(stdout)强制输出当前输出缓冲区中的内容,一些在Debug下一些莫名其妙的error可以用fflush(stdout)立即输出在处理过程中的中间结果来确定error所在。
int fflush(FILE*fp);
功能:将缓冲区中任何未写的数据发送到一个由fp指定的输出文件中去。这个过程称为刷新缓冲区。
参数:只有一个参数,FILE对象指定的一个缓冲流。
返回值:如果成功,该函数返回零值,如果发生错误,则返回EOF,且设置错误标识符(EOF)。
fflush函数可以将缓冲区中任何未写入的数据写入文件中。不加的话都在内容都保存在了缓冲区,只有在调用fclose()函数的时候才写进去!
由于fflush是实时 的将缓冲区的内容写入磁盘,所以不要大量去使用,但如果是特别敏感的数据,可以通过fflush写入磁盘,防止由于电脑各种故障,内存的数据丢失。
注意:如果fp是一个空指针,将刷新所有的输出缓冲。对一个输入流使用fflush()函数的效果没有定义。只要最近一次使用流的操作不是输入操作,就可以对一个更新流(任何读-写模式的流)使用这个函数。
9、检查读取文件失败的原因。当标准输入函数返回EOF时,通常表示已经到达了文件结尾。可是,这也有可能表示发生了读取错误。用这两个函数int feof(FILE *fp)和int ferror(FILE *fp)来区分这两种可能性。
(1)int feof(FILE *fp);
功能:查看是否已经到达了文件结尾
参数:只有一个参数,文件指针。
返回值:如果最近一次输入调用检测到文件结尾,返回一个非零值,否则返回零值。
(2)int ferror(FILE *fp)
功能:检查是否发生了读取错误
参数:只有一个文件指针
返回值:如果发生读写错误,ferror()函数返回一个非零值,否则返回零值。
------------恢复内容结束------------