【C语言】文件 *** 作

【C语言】文件 *** 作,第1张

        因为数据如果只存储在内存中,程序结束后数据就被摧毁了,为了能够让数据持久化的保留,这里就需要把数据存储在文件中,需要使用的时候直接从文件中读取出来使用。




一、文件指针

        在内存中有个专门来进行文件交互的区域,使用FILE*类型的指针来控制 *** 作文件,FILE *是个结构体指针。


struct _iobuf {  
        char *_ptr;  
        int   _cnt;  
        char *_base;  
        int   _flag;  
        int   _file;  
        int   _charbuf;  
        int   _bufsiz;  
        char *_tmpfname;  
        };  
typedef struct _iobuf FILE;  

 流(stream):

        流是一个很抽象的概念,可以理解为水流,为了方便所有外部设备的输入和输出,在其连接的上层封装了一个流。


当程序需要读取数据的时候,就会开启一个通向数据源的流。


这个数据源可以是文件,内存,或是网络连接。


        类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。


这时候你就可以想象数据好像在其中“流”动一样。


        其包括文件流、标准输入流(stdin 键盘)、标准输出流(stdout 屏幕)、标准错误流(stdrerr 屏幕)        

        那么对于 *** 作文件来说,就要使用文件的流。


   

FILE* pf=fopen("test.txt","r");//以读的方式打开文件

         这里pf指针就可以理解为打开了文件test.txt并与文件流产生了连接。


注意这里最好判断pf是不是为NULL,如果为NULL说明打开文件失败

if(pf==NULL)
    printf("文件打开失败\n");

         这样就可以从文件读取数据或者写入数据了。


而这里根据fopen()的第二个参数,来选择对文件的具体 *** 作

字符串

说明

r

以只读方式打开文件,该文件必须存在。


r+

以读/写方式打开文件,该文件必须存在。


rb+

以读/写方式打开一个二进制文件,只允许读/写数据。


rt+

以读/写方式打开一个文本文件,允许读和写。


w

打开只写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。


w+

打开可读/写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。


a

以附加的方式打开只写文件。


若文件不存在,则会创建该文件;如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(EOF 符保留)。


a+

以附加方式打开可读/写的文件。


若文件不存在,则会创建该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(EOF符不保留)。


wb

以只写方式打开或新建一个二进制文件,只允许写数据。


wb+

以读/写方式打开或新建一个二进制文件,允许读和写。


wt+

以读/写方式打开或新建一个文本文件,允许读和写。


at+

以读/写方式打开一个文本文件,允许读或在文本末追加数据。


ab+

以读/写方式打开一个二进制文件,允许读或在文件末追加数据。


        有的文件 *** 作会自己生成文件,如果fopen()第一个参数只写了文件名,其生成在同.c或者.cpp文件路径下 。


当然有的命令不会自己创建文件的话自己创建也可以。


        

         fopen()其第一个参数可以如果只使用文件名,其称为相对路径。


如果加上经过的文件和盘符,其叫做绝对路径

         对于绝对路径来说,其文件不用存放在同代码文件目录中,可以在任意地方。



二、文件 *** 作函数:

int ch = fgetc(pf);   //从文件接受一个字符
fputc(ch,pf);         //然后输出到文件里
​

int ch = fgetc(stdin);   //从键盘接受一个字符
fputc(ch,stdout);        //然后输出到屏幕上

fputs("abcdef\n",pf); //写一行

char arr[256]={0}; 
fgets(arr,256,pf);    //只读一行,遇到\n结束。


只读255个,最后一个是\0 while(fgets(arr,256,pf)!=NULL){ printf("%s\n",arr); }

PS:上面的函数和这里fputs()跟fgets()函数,不仅可以从文件中读取和写入一行数据,还可以使用标准输入流,标准输出流从键盘上读取输出到屏幕上。


写入文件一行字符串。


 

从文件读取一行。


 

struct Stu
{
    char name[20];
    int age;
    double score;
}s={"张三","15",85.5};

//所有输出流的格式化输出(到文件):
fprintf(pf,"%s %d %lf",s.name,s.age,s.score);
//所有输入流的格式化(从文件)输入:
fscanf(pf,"%s %d %lf",s.name,&(s.age),&(s.score));

         可以发现这两个函数跟printf()和scanf()非常相似。


fprintf(stdout,"%s %d %lf",s.name,s.age,s.score);
//相当于printf();
fscanf(stdin,"%s %d %lf",s.name,&(s.age),&(s.score));
//相当于scanf();

        但是其实这两个函数跟 sprintf()和 sscanf()最为相似。


只不过一个是文件 *** 作,一个是字符串 *** 作。


char buf[256]={0};
sprintf(buf,"%s %d %lf",s.name,s.age,s.score);
//把一个格式化的数据转换成字符串
sscanf(buf,"%s %d %lf",s.name,&(s.age),&(s.score));
//从一个字符串中提取出格式化的数据

二进制写文件 :
fwrite(&s,sizeof(buf),1,pf);

fread(&s,sizeof(buf),1,pf);

        二进制写文件(注意用wb格式),是把数据转换成二进制形式,不做任何更改,直接输出到文件。


但是由于文本文件.txt不能解析二进制,所以会出现乱码。



三、随机读写:指定位置读写

fseek();
SEEK_CUR;//文件当前的位置
SEEK_END;//文件末尾的位置
SEEK_SET;//文件开始的位置

       文件数据有数据 a b c d e

//随机读
int ch = fgetc(pf);
fseek(pf, 2, SEEK_CUR);
ch = fgetc(pf);

        这里读取了一个字符后,文件指针指向b,使用函数fseek()使指针在本位置,往后偏移2,指向d。


最后ch得到的就是字符d。


        刚开始初始化指针pf时,指针指向文件最开始a,读一个字符,指针往后偏移1。


fseek(pf,-2,SEEK_END);

PS:使用负数是往前偏移,在指针指向文件开头的时候不要往前偏移。


指向文件时不要往后偏移。


//随机写
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
fputc('d', pf);
fseek(pf, -3, SEEK_CUR);
fputc('w', pf);

这样会把指针再次指向字符b的位置,然后把b改为w。


最后的文件存的结果就是awcd

         当不知道文件指针指向何处的时候,可以用库函数来查看。


long int ftell ( FILE * stream );

          返回文件指针相当于起始位置的偏移量

void rewind ( FILE * stream );

           让pf指向起始地址


四、判断读取结束        

        是根据判断读取函数的返回值来判断的。


while(ch=getc(pf)!=EOF){
};

while(fgets(arr,256,pf)!=NULL){
};

while(fread(buf,4,1,pf)<1){
};

         fread()当读到的数据已经小于1说明读取结束或者读取错误。


这时候可以使用feof()和ferror()两个函数来判断,是读到文件末尾结束的,还是读文件出错而结束的。


if(feof(pf)){
    printf("文件读取结束\n");
}
else if(ferror(pf)){
    printf("文件读取错误\n");
}

PS:EOF是end if file的缩写,其意思为文件的末尾。


其本质为一个宏。


 

  


五、缓冲区概念

        文件在读取和写入的时候,并不是一个一个的写入和读取,这样效率低。


而是先把数据放到缓冲区,够一定数量后,一起写入或者读取。


fputs("abcde",pf);
Sleep(2000);
fflush(pf);//刷新缓冲区,把数据放到硬盘中去

        这里使程序在abcde写入缓冲区,还未刷新缓冲区时,打开文件,发现文件上并没有数据。


       

        等待20秒后,刷新了缓冲区,发现数据写入了文件。


PS:Sleep(20000)函数使程序休眠20秒,里面的单位是毫秒。


        fflush()用来刷新缓冲区。


fclose()函数也有刷新缓冲区的功能。


FILE* pf=fopen("test.txt","w");
fclose(pf);

        因为缓冲区的存在,所以 *** 作文件完成后必须使用fclose()关闭文件,防止后面继续 *** 作文件时会发生错误。



总结:

        文件 *** 作使用的频率不高,通常在写程序日志,为了方便查看程序运行情况的时候使用。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/578203.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-11
下一篇 2022-04-11

发表评论

登录后才能评论

评论列表(0条)

保存