文件操作详解

前言

1.本文介绍文件相关操作与知识。编译环境:VS2019

2.博主收录了一些资料,有需要自拿New Young
学习中的问题收录与解答:New Young

文件的意义

电脑的内存是有限的,内存可以说先占用再释放再占用的对象。一个程序结束后,它所使用的内存,会被销毁,继续为其它程序使用。这也就意味着程序所产生的数据会消失,数据不能持久化。为了做到数据的持久化。我们可以将数据存放在硬盘文件,数据库等

以通讯录为例,如果我们不能及时将内存中的用户数据及时存放到可以使数据持久化的地方—文件中,那么这个程序对我们来说是失败的。

文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用

文件标识=文件路径+文件名主干+文件后缀

为了方便起见,文件标识常被称为文件名 。

  • 文件路径有2种:相对路径与绝对路径

  • 绝对路径:是从盘符开始的路径,形如

    C:\Users\Administrator\Desktop\试体\Project9

  • 相对路径:从当前目录或者路径开始的路径。

    这里源.cpp在project9下,它的相对路径:project9\源.cpp.

    image-20220120121722285

  • 如果在vs中访问某个文件时,系统会先从当前目录下开始查找文件。

文件分类

  • 按文件功能:主要分为:程序文件和数据文件。
  • 程序文件:包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。

    文件的后缀默认是不显示的,如果需要显示可以通过这张图继续设置,另外如果默认情况的后缀,在设置文件名是不要人为的加后缀.

image-20220120122810402

  • 数据文件

文件的内容不一定是程序,而是程序运行时读写的数据,比如程 序运行需要从中读取数据的文件,或者输出内容的文件

其中数据文件按内容分类: 文本文件和二进制文件

  • 数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。—文件中的数据有些我们看不懂,是一堆能被计算机识别的信息,而人不行。

image-20220120142027313

  • ​ 可以通过VS2019上的二进制编译器查看二进制文件中的信息

image-20220120142416356

  • 从vs2019可以看出 20在内存中的二进制不加转换的存入文件时,二进制信息是按小端字节序。存放到二进制文件中的。

  • 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。 —文件中的数据,我们可以直接看明白的

image-20220120141846724

文件的输入输出方向判断

引例

在使用scanf时,我们通过 键盘将文本信息送到屏幕上(其实会将这些信息先送到一个中转站–缓冲区),之后程序将这些信息按自己的需求加载到内存—-这一过程称为–将信息输入到内存

在使用printf时,我们将格式化的信息打印到屏幕上—这一过程称为–将信息输出到屏幕。

简记站在内存的角度,如果我们向外送数据就是输出,如果需要数据,就是输入。

image-20220120124945045

文件的输入与输出:

同理,站在内存的角度,如果我们把数据送到文件中—文件输出,如果需要把文件中的内容读入到内存—文件的输入。

image-20220120125726651

  • 流是个抽象的概念,我们在熟悉它的过程,需要先知道它的作用,不用过多的了解它的底层细节。

  • 流具有方向性,至于是输入流还是输出流则是一个相对的概念,一般以程序为参考,如果数据的流向是程序至设备,我们成为输出流,反之我们称为输入流。

  • 我们可以把”流“想象成一个中转站—像水管一样的,它会自动将信息运送至相应的地方。

image-20220120144835270

image-20220120145208882

  • 以文件为例:如果数据流行文件,是文件输出流,反之文件输入流----这个流称为文件流。

  • 标准流是一个很特殊的流,因为在一个C程序中,会默认自动打开标准流:标准输出流,标准输入流,标准错误流。本质其实是文件流,关于文件流的函数都适用于它。

  • 标准输出流(stdout):内存中的信息加载到屏幕上,

  • 标准输入流(stdin):以键盘为媒介将输入的文本信息加载到内存中。

  • 标准错误流(stderr):将错误信息加载到屏幕上。

文件信息区

  • 每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE .

  • *一般我们通过使用FILE 的文件指针,来建立指针与文件的关系,这样就可以操作文件了

  • 不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,**使用者不必关心细节 **

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

文件的打开与关闭

​ 打开文件函数

FILE * fopen ( const char * filename, const char * mode );
  • 成功返回指向文件的指针,否则返回NULL。

    image-20220120153040641

  • 访问文件,我们只需要向fopen函数传入文件的相对路径或者绝对路径即可—路径相当与地址,知道地址,就可以操作文件了。

  • 我们访问文件时,是以读(read)的形式(即只读文件中的数据,不改变文件中数据或者输出数据到文件),还是以写(write)的形式(向文件中输出数据),还是以读和写(read和write)的形式访问文件,这需要通过fopen形参中的 mode控制–(下面会具体介绍)。

关闭文件

int fclose ( FILE * stream )
  • 关闭文件,成功返回0,失败返回EOF。

    image-20220120153059048

文件打开方式汇总与规律总结

FILE * fopen ( const char * filename, const char * mode );

打开方向–mode

文件使用发生 含义 如果文件不存在
“r”(只读) 为了输入数据,打开一个已经存在的文本文件 出错
“w”(只写) 为了输出数据,打开一个文本文件 建立一个新的文件
“a”(追加) 向文本文件尾添加数据 建立一个新的文件
“rb”(只读) 为了输入数据,打开一个二进制文件 出错
“wb”(只写) 为了输出数据,打开一个二进制文件 建立一个新的文件
“ab”(追加) 向一个二进制文件尾添加数据 出错
“r+”(读写) 为了读和写,打开一个文本文件 出错
“w+”(读写) 为了读和写,建议一个新的文件 建立一个新的文件
“a+”(读写) 打开一个文件,在文件尾进行读写 建立一个新的文件
“rb+”(读写) 为了读和写打开一个二进制文件 出错
“wb+”(读写) 为了读和写,新建一个新的二进制文件 建立一个新的文件
“ab+”(读写) 打开一个二进制文件,在文件尾进行读和写 建立一个新的文件

规律总结:

  • 与”w“(写)有关的mode(”w“,”wb“,”w+“,”wb+“),如果文件存在,都会在打开指定文件时,清空原文件中的内容,如果不存在,都会建立一个同名的指定文件。

  • 与”r“(读)有关的mode(”r“,”rb“,”r+“,”rb+“),如果文件不存在,fopen返回NULL.

  • 与”a“(追加)有关的mode(”a“,”ab“,”a+“,”ab+“)。如果文件存在,则从文件尾开始追加数据,如果不存在,则建立一个同名的指定文件。

文件的顺序读写函数

补充:文件信息区中有一个记录指针char *_ptr–可以指向读和写的起始位置。已读或已写时,记录指针会自动改变,以指向为读或未写的起始区域

image-20220120153529408

文本文件

fgetc,getchar,getc,gets,fgets,gets_s(vs独有)对比

函数 函数功能 形参分析 返回值 注意点 适用与
int fgetc( FILE *stream ); 函数功能:向任意流(文件流,标准流stdout)中读取一个字节大小的字符。 传入的是文件文件指针,因为标准输入流stdin本质也是FILE*–文件流。因此有 fgetc(stdin); fgetc()返回的是读到字符的ASICC值,如果读取失败或者遇到文件尾返回EOF。 注意:空白键(回车,空格,tabl都是一个字符),在使用fgetc()时要多留意空白键。 文件流,stdin
int getc(FILE*stream); 同fgetc() 同fgetc() 同fgetc(). 同fgetc() 文件流,stdin
int getchar (void); 从标准输入流stdin中读入一个字符 无参数,但是因为一个程序默认启动3个标准流,因此默认从stdin中读入一个字符 返回读入字符的ASICC值,如果读取失败或者遇到文件尾返回EOF。 1.默认从stdin读入字符;2.空白键(回车,空格,tabl都是一个字符),在使用getchar()时要多留意空白键。 stdin
char * gets(char*buffer); 从标准输入流stdin中读取字符串,存入到buffer指向的内存中 char*的指针 如果读取成功,返回buffer,否则NULL. 因为gets在从stdin中读入时只以第一个回车符(\n)为结束,同时在最后将’\n‘换成‘\0’,这也就是意味着,gets是没有限度,这就很可能导致buffer的溢出,导致creash。因此一般用fgets()代替gets。 stdin
char *gets_s(char *buffer,size_t n). 从buffer中读入n-1(最多)个字符 成功返回buffer,否则EOF。 1. 从char*的buffer,而不是FILE *的stdin。2.与gets一样也是以\n为结束,并将\n换成\0.因此这也可能会导致creash。但比gets,安全一点。因此更推荐fgets。 stdin
char * fgets(char*string,int n,FILE * stream); 从文件流.标准流中读取n-1(最多)个字符到string中, string 非空。n代表要从strram中读入的字符个数。stream:文件流,或者标准流 如果成功,返回字符串string。否则返回NULL以指示错误或文件结束条件 1… fgets从当前流位置到第一个换行符(包括第一个换行符)、到流末尾的字符,或者直到读取的字符数等于n-1(以先到者为准);2… fgets最多读入n-1个字符,并在存储的字符串最后会加入一个\0.;3.与gets一样,fgets会把回车符吸收,只是不改变它,gets会将其变为\0。 文件流,stdin

image-20220120232600363

image-20220120234205582

putc,putcahr,fputc,puts,fputs对比

函数 函数功能 形参分析 返回值 注意点 代码截图 适用
int putchar(int c); 向标准输出流stdout,输出c 成功返回输出c的值,防止EOF 标准输出流stdout是默认打开的 stdout
int putc(int c,FILE*stream); 向文件流,或者stdout,输出字符c stream可以是文件流,也可以是stdout 成功返回输出c的值,防止EOF 文件流,stdout
int fputc(int c,FILE *stream); 向文件流,或者stdout,输出c stream可以是文件流,也可以是stdout 成功返回输出c的值,防止EOF 如果在文件的中间进行写入,则替换指定位置的数据。 image-20220120210030747 文件流,stdout
int fputs(const char* string,FILE *stream); 向文件流,或者stdout,输出字符string stream可以是文件流,也可以是stdout 如果成功,返回一个非负值。出错时,返回EOF。 如果是标准输出流在输出字符串后,会自动输出一个换行符‘\n’,文件流不会。 文件流,stdout
int puts(const char *string); 向stdout输出字符串string 如果成功,返回一个非负值。出错时,返回EOF。 在输出字符串后,会自动输出一个换行符‘\n’; stdout

image-20220120225036744

image-20220120225113249

scanf,fscanf,sscanf对比

函数 函数功能 形参分析 返回值 注意点 代码截图 适用
int scanf(const char *format [,argument]…); 从stdin中读入格式化的数据 返回按格式读入成功的项目数。 1.一旦不是按格式读入的数据,就停止读入且之后的运算是无意义的。2.因为返回值的意义,在使用时,为防止出错,需要对返回值判断。 image-20220121000159460 stdin
int fscanf(FILE*stream,const char *format [,argument]…); 从文件流,stdin中读入格式化的数据 返回按格式读入成功的项目数。返回值0表示未分配任何字段或者在第一次分配就错误。如果出现错误或在第一次转换之前到达字符串末尾,则返回值为EOF。 1.一旦不是按格式读入的数据,就停止读入且之后的运算是无意义的。2.因为返回值的意义,在使用时,为防止出错,需要对返回值判断。 image-20220121001236852image-20220121001407038 文件流,stdin
int sscanf(const char*buffer,const char *format [,argument ] … ); 从字符串buffer中读取格式化的数据 绝大部分都是返回成功读取的项目数。出现错误时,停止读取,并返回已正确读写的项目数。只有当第一次读写就碰到\0,才返回EOF 1.使用时,要判断返回值。2.返回EOF的情况只有一种:第一次就碰到\0 image-20220121095840034 字符串

printf ,fprintf ,sprintf对比

函数 函数功能 形参分析 返回值 注意点 代码截图 适用
int printf(const char *format [, argument]…); 向stdout输出格式化的数据 返回打印的字符数,如果出现错误,则返回负值。 stdout
int fprintf(FILE*stream,const char *format [, argument]…); 向文件流或者stdou中加载格式化的数据 返回打印的字符数,如果出现错误,则返回负值。 image-20220121112813121 文件流,标准输出流-stdout
int sprintf(char *buffer,const char *format [, argument]…); 向buffer,加载格式化二点数据 Sprintf返回存储在缓冲区中的字符数,不包括终止空字符。 1.buffer不能为NULL,且不能是常量字符串指针2.sprintf以\0为存储结束标志,这也就可能会到buffer溢出,导致creash。因此在使用时,使用者要保证能够不溢出。 image-20220121101547836image-20220121102323507 字符串

二进制文件

fread和fwrite

函数 函数功能 形参分析 返回值 注意点 代码截图
size_t fread(void * buffer,size_t size,size_t count,FILE stream); 从文件流(二进制文件)读取count个size个字节大小的数据到buffer中 Fread返回实际读取的完整项目数,如果发生错误或在达到Count之前遇到文件末尾,则该值可能小于Count;如果SIZE或COUNT为0,则Fread返回0,缓冲区内容保持不变;使用FEOF或FERROR函数来区分读取错误和文件结束条件 1.该函数是从stream,一个字节一个字节放到buffer;2.不用把它用于stdin中,会出现不可知的错误。image-20220121103651047 image-20220121104211029 二进制文件流,
size_t fwrite(void * buffer,size_t size,size_t count,FILE stream); 向二进制文件一次性加载 count个size字节大小的数据 Fwrite返回实际写入的完整项目数,如果发生错误,该值可能小于count。此外,如果发生错误,则无法确定文件位置指示符。 1.在使用时,尽量对二进制文件使用,对文本文件使用会有一些问题。 image-20220121112239515 二进制文件流

文件的随机读写函数

要想做到随机读写,需要做到能随时改变 文件信息区中的 char *ptr的起始位置。

fseek();

  • int seek(FILE*stream,long offset,int orgin);

  • 根据文件指针的位置和偏移量来 重新定位ptr。

  • offset 偏移量:所有定位的位置到 orgin的相对距离。

    image-20220121113829065

  • orign 起始位置有是三个:

    • SEEK_CUR—ptr当前位置

    • SEEK_END—-文件尾的位置

    • SEEK_SET—-文件起始位置

image-20220121115114370

ftell()

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

image-20220121115751731

rewind()

  • void rewind ( FILE * stream);
  • 让ptr的位置回到文件的起始位置 .

image-20220121120140704

文件读取结束的判定

feof与ferror

  • 应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束
  • 注意不能用它判断文件读取是否结束。
  • 如果想判断是否到达文件尾就用feof,判断读入错误用ferror。
  1. . 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
    例如:

    • fgetc 判断是否为 EOF。fgetc几乎不可能失败,毕竟任何字符都可以读取

      image-20220121121655094

    • fgets 判断返回值是否为 NULL ,即使遇到文件结束符,fgets仍正常返回字符串地址。

      image-20220121121432527

    • fscanf();用feof判断文件尾,用返回值个数判断是否出错。

      image-20220121122732430

  2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
    例如:

    • fread判断返回值是否小于实际要读的个数。

      image-20220121124218160

文件缓冲区

  • 引例:如果我们是在学校宿舍楼,你是选择将接下来要干的事都想好再下楼,还是想到一件下去一次呢?
  • 同样如果我们读入一个文件中的内容,应该也是先将一大部分内容放到一个中转站—文件缓冲区,这样就可以提高时间的高效利用率。

image-20220121124524571

  • 因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区(fflush函数)或者在文件操作结束的时候关闭文件。如果不做,可能导致读写文件的问题

image-20220121124747680

总结

掌握文件的读写操作,理解流的概念,在使用某些函数时要留意返回值问题。
如需转载请标明出处:New Young

猜你喜欢

转载自blog.csdn.net/qq_55439426/article/details/122619241