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()函数返回一个非零值,否则返回零值。

------------恢复内容结束------------