c++基础之文件操作

一.认识文件

什么是⽂件? 磁盘(硬盘)上的⽂件是⽂件。 但是在程序设计中,我们⼀般谈的⽂件有两种:程序⽂件、数据⽂件(从⽂件功能的⻆度来分类 的)。

1.程序⽂件 :程序⽂件包括源程序⽂件(后缀为.c),⽬标⽂件(windows环境后缀为.obj),可执⾏程序(windows 环境后缀为.exe)。

2. 数据⽂件 :⽂件的内容不⼀定是程序,⽽是程序运⾏时读写的数据,⽐如程序运⾏需要从中读取数据的⽂件,或 者输出内容的⽂件。本章讨论的是数据⽂件。 在以前各章所处理数据的输⼊输出都是以终端为对象的,即从终端的键盘输⼊数据,运⾏结果显⽰到 显⽰器上。 其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使⽤,这⾥处 理的就是磁盘上⽂件。

3.⽂件名
⼀个⽂件要有⼀个唯⼀的⽂件标识,以便⽤⼾识别和引⽤。
⽂件名包含3部分:⽂件路径+⽂件名主⼲+⽂件后缀
例如: c:\code\test.txt
为了⽅便起⻅,⽂件标识常被称为⽂件名。

二. ⼆进制⽂件和⽂本⽂件?

根据数据的组织形式,数据⽂件被称为⽂本⽂件或者⼆进制⽂件。

数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的⽂件中,就是⼆进制⽂件。

如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是⽂ 本⽂件。

⼀个数据在⽂件中是怎么存储的呢?

字符⼀律以ASCII形式存储,数值型数据既可以⽤ASCII形式存储,也可以使⽤⼆进制形式存储。

如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽ ⼆进制形式输出,则在磁盘只占四个字节。

 这里以vs2013为例,打开二进制文件

 三.文件的打开与关闭

1. 流和标准

①流

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出

操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作,我们抽象出了流的概念,我们可以把流

想象成流淌着字符的河。

C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。

⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。

②标准流
那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语⾔程序在启动的时候,默认打开了3个流:
•stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。
•stdout - 标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出
流中。
•stderr - 标准错误流,⼤多数环境中输出到显⽰器界⾯。
这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。
stdin、stdout、stderr 三个流的类型是: FILE * ,通常称为⽂件指针。
C语⾔中,就是通过 FILE* 的⽂件指针来维护流的各种操作的。

4.2 ⽂件指针
缓冲⽂件系统中,关键的概念是“⽂件类型指针”,简称“⽂件指针”。
每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名
字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系
统声明的,取名 FILE.
例如,VS2013 编译环境提供的 stdio.h 头⽂件中有以下的⽂件类型申明:
struct _iobuf {

 char *_ptr;

 int _cnt;

 char *_base;

 int _flag;

 int _file;

 int _charbuf;

 int _bufsiz;

 char *_tmpfname;

};

typedef struct _iobuf FILE;
不同的C编译器的FILE类型包含的内容不完全相同,但是⼤同⼩异。
每当打开⼀个⽂件的时候,系统会根据⽂件的情况⾃动创建⼀个FILE结构的变量,并填充其中的信
息,使⽤者不必关⼼细节。
⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使⽤起来更加⽅便。

下⾯我们可以创建⼀个FILE*的指针变量:

1 FILE* pf;//⽂件指针变量
定义pf是⼀个指向FILE类型数据的指针变量。可以使pf指向某个⽂件的⽂件信息区(是⼀个结构体变量)。通过该⽂件信息区中的信息就能够访问该⽂件。也就是说,通过⽂件指针变量能够间接找到与它关联的⽂件。


 

 

3 .⽂件的打开和关闭

⽂件在读写之前应该先打开⽂件,在使⽤结束之后应该关闭⽂件。

在编写程序的时候,在打开⽂件的同时,都会返回⼀个FILE*的指针变量指向该⽂件,也相当于建⽴了 指针和⽂件的关系。

ANSI C 规定使⽤ fopen 函数来打开⽂件, fclose 来关闭⽂件。

//打开⽂件
FILE * fopen ( const char * filename, const char * mode );
//关闭⽂件
int fclose ( FILE * stream );
 

mode表⽰⽂件的打开模式,下⾯都是⽂件的打开模式:

 四.⽂件的顺序读写

顺序读写函数介绍
 
1.fgetc 
函数原型: int fgetc(FILE *stream); 
结构组成:
 
- 参数: FILE *stream  用于指定从哪个文件流(如文件、标准输入流  stdin  等 )读取字符。
 
- 返回值:返回读取到的字符(提升为  int  类型 ),文件末尾或出错时返回  EOF  。
 
- 内部逻辑:从  stream  指向的输入流中逐个读取字符,每次读取一个。
 
2.fputc
函数原型: int fputc(int c, FILE *stream); 
结构组成:
 
- 参数: int c  是要写入的字符(以  int  形式传递 ), FILE *stream  确定向哪个文件流(文件、标准输出流  stdout  等 )写入。
 
- 返回值:成功写入返回写入的字符( int  形式 ),失败返回  EOF  。
 
- 内部逻辑:将字符  c  写入到  stream  指向的输出流中。
 
3.fgets
函数原型: char *fgets(char *s, int n, FILE *stream); 
结构组成:
 
- 参数: char *s  是用于存储读取文本行的字符数组指针; int n  规定最多读取  n - 1  个字符(留位置给  '\0'  ); FILE *stream  指明从哪个输入流读取。
 
- 返回值:成功返回  s  (字符数组指针 ),文件末尾或出错返回  NULL  。
 
- 内部逻辑:从  stream  指向的输入流中按行读取文本,读取到换行符或达到  n - 1  个字符时停止。
 
4.fputs
函数原型: int fputs(const char *s, FILE *stream); 
结构组成:
 
- 参数: const char *s  是要写入的字符串指针, FILE *stream  用于指定输出流(文件、 stdout  等 )。
 
- 返回值:成功返回非负整数(一般是写入字符数 ),失败返回  EOF  。
 
- 内部逻辑:把字符串  s  写入到  stream  指向的输出流,不写字符串结束符  '\0'  。
 
5.fscanf
函数原型: int fscanf(FILE *stream, const char *format, ...); 
结构组成:
 
- 参数: FILE *stream  确定输入流来源; const char *format  是格式控制字符串,规定读取数据格式; ...  是可变参数列表,对应格式说明符的变量地址。
 
- 返回值:返回成功匹配并赋值的输入项数,文件末尾或出错返回  EOF  。
 
- 内部逻辑:按照  format  规定的格式,从  stream  指向的输入流读取数据并存储到对应变量。
 
6.fprintf
函数原型: int fprintf(FILE *stream, const char *format, ...); 
结构组成:
 
- 参数: FILE *stream  指明输出流目标; const char *format  为格式控制字符串,决定输出数据格式; ...  是可变参数列表,是要输出的数据项。
 
- 返回值:返回成功写入的字符数,出错返回负数。
 
- 内部逻辑:依据  format  格式,将可变参数列表中的数据格式化后写入  stream  指向的输出流。
 
7.fread
函数原型: size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 
结构组成:
 
- 参数: void *ptr  是存储读取数据的缓冲区指针; size_t size  为每个数据块大小(字节 ); size_t nmemb  是要读取的数据块数量; FILE *stream  是输入流文件指针。
 
- 返回值:返回实际读取的数据块数量,小于  nmemb  可能是文件末尾或出错。
 
- 内部逻辑:从  stream  指向的文件输入流中,按指定大小和数量读取二进制数据到  ptr  指向的缓冲区。
 
8.fwrite
函数原型: size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 
结构组成:
 
- 参数: const void *ptr  是要写入数据的缓冲区指针; size_t size  为每个数据块大小(字节 ); size_t nmemb  是要写入的数据块数量; FILE *stream  是输出流文件指针。
 
- 返回值:返回实际写入的数据块数量,小于  nmemb  表示写入出错。
 
- 内部逻辑:将  ptr  指向缓冲区中的数据,按指定大小和数量写入到  stream  指向的文件输出流。

示例:

这段 代码开头通过宏定义屏蔽了一些安全警告,随后引入必要头文件。 test  函数尝试以写入模式打开 data.txt  文件,若失败则输出错误信息并返回;成功打开后写入字符串 “abcd”,若写入失败也输出错误信息并关闭文件。 main  函数先调用 test  函数,接着以读取模式打开 data.txt ,若成功则读取最多2个字符到字符数组 pc  并逐个输出,若读取失败则输出错误信息并关闭文件。代码完整展现了文件的读写操作流程及错误处理机制 。

 

五.文件的随机读写

1.文件指针与位置标记
在C语言中,每个打开的文件都关联着一个文件指针( FILE * 类型) ,系统为文件内部维护着一个位置标记,指向文件中当前读写的位置。对于顺序读写,位置标记按顺序移动;而随机读写可通过函数改变位置标记,实现对文件任意位置的操作。
 
2.实现随机读写的函数
 fseek 函数
- 功能:用于改变文件位置标记的位置。通过指定从文件开头( SEEK_SET  )、当前位置( SEEK_CUR  )或文件末尾( SEEK_END  )开始,移动一定字节数( offset  )。例如, fseek(fp, 10, SEEK_SET);  表示将文件指针 fp 指向的文件的位置标记移动到距离文件开头10个字节处。
- 适用场景:适用于二进制文件,因为文本文件在不同系统下换行符等字符存在转换,可能导致偏移量计算不准确。
 ftell 函数
- 功能:用来获取文件位置标记相对于文件开头的当前位置,返回值为长整型,表示字节数。比如在文件读写过程中,可通过 ftell 函数记录当前位置,方便后续操作。
- 应用:常用于需要记录文件读写进度,或后续要回到特定位置继续读写的场景。
 rewind 函数
- 功能:将文件位置标记重新定位到文件开头。它等价于 fseek(fp, 0L, SEEK_SET);  ,但更简洁直观。
- 使用场景:当需要重新从文件开头进行读写操作时,可使用该函数。
 
3.操作流程
首先使用 fopen 函数以合适的模式(如 r+  、 w+  、 a+ 等)打开文件。
根据需求,使用 fseek 函数将文件位置标记移动到目标位置。
使用 fread 函数从指定位置读取数据,或使用 fwrite 函数向指定位置写入数据。
操作完成后,使用 fclose 函数关闭文件。
 
4.注意事项
在使用 fseek 函数时,要注意偏移量的计算,特别是在处理文本文件时,换行符等可能带来的字节数差异。
随机读写操作可能改变文件原有内容布局,要谨慎操作,避免数据丢失或文件损坏。
确保文件打开模式具有相应的读写权限,否则随机读写操作可能失败。

六. ⽂件读取结束的判定

1 .被错误使⽤的 feof

牢记:在⽂件读取过程中,不能⽤feof函数的返回值直接来判断⽂件的是否结束。

feof 的作⽤是:当⽂件读取结束的时候,判断是读取结束的原因是否是:遇到⽂件尾结束。

1. ⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )

例如:

•fgetc 判断是否为 EOF .

•fgets 判断返回值是否为 NULL .

⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。
例如:
•fread判断返回值是否⼩于实际要读的个文件

七.文件缓冲区

缓冲区是在内存区预留的空间,用于暂时存放文件读写期间的数据 。

 在计算机系统里,数据的流转离不开内存与硬盘。程序运行时,内存中划分出了不同区域。其中,程序数据区存放着程序运行相关的数据。当数据要从硬盘读取到程序中时,会先进入内存里的输入缓冲区,再提供给程序数据区使用。而当程序要把数据存回硬盘时,数据会先暂存在输出缓冲区,之后再写入硬盘。这就像在硬盘和程序数据区之间,分别设置了两个“中转站”,让数据的传输更高效、有序。

以下是详细介绍:

1.作用

- 提升读写效率:减少对外部存储设备(如硬盘)的直接读写次数。比如读文件时,不是每次读操作都直接从硬盘读取少量数据,而是先将一批数据读入缓冲区,后续从缓冲区获取,降低硬盘I/O操作频率 ;写文件时,数据先存缓冲区,满了或特定条件下再写入硬盘。

- 缓和速度差异:缓和CPU运算速度与I/O设备读写速度不匹配的矛盾。CPU运算快,I/O设备读写慢,有了缓冲区,CPU不用等待I/O设备读写完成,可继续执行其他任务 。

2.分类

- 输入缓冲区:读文件时,硬盘等存储设备的数据先读入此缓冲区,再供程序使用 。

- 输出缓冲区:写文件时,程序数据先存入该缓冲区,满足一定条件(如缓冲区满、执行刷新操作等)时,再写入外部存储设备 。

3.刷新方式

- 行缓冲:遇到换行符 \n  时,将缓冲区数据刷新到目标位置(如显示器、文件等) 。常用于标准输出(显示器)等场景,如 printf 函数默认行缓冲,输出内容有 \n 时才显示 。

- 全缓冲:缓冲区被数据填满后,才将数据刷新到目标位置 。文件操作多采用这种方式,可减少磁盘I/O次数,提高效率 。

- 无缓冲:数据不暂存缓冲区,直接进行读写操作 。较少用,如 stderr 通常无缓冲,确保错误信息及时输出 。

4.相关操作

- 关闭文件: fclose 函数关闭文件时,会自动刷新缓冲区,保证缓冲区数据写入文件 。

- 手动刷新:使用 fflush 函数可手动刷新输出缓冲区,如 fflush(stdout) 刷新标准输出缓冲区 ,让数据立即显示。数

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值