1. 文件的作用
我们通常通过文件将我们的资料或者代码给保存到电脑的硬盘中。如果不使用程序的话我们的所运行的程序在推出后就会丢失数据,这是因为我们运行的时候他使用的是电脑的内存,在退出程序后内存会被回收,数据也就随之而去了,再次运行程序就找不到之前的数据了。
所以在这种情况下,文件的作用就体现出来了。可以通过创建一个文件来保存之前的数据,这样的话在下次运行程序的时候就可以使用之前的数据了,将数据做到了持久化的保存。
2. 二进制文件和文本文件
2.1 数据⽂件
在C语言文件操作中主要学习的是对于数据文件的操作
数据文件不是用来保存程序的,而是用来保存程序运行后产生的数据
2.2 文本文件
文本文件是以人类可读的字符形式存储数据的文件。这意味着它们通常包含文本内容,比如字母、数字、标点符号等。文本文件可以被文本编辑器打开并直接阅读,因为它们使用了人类可识别的字符编码(比如ASCII或Unicode)来表示数据。
在C语言中,使用标准的文件I/O函数(比如fprintf
、fscanf
、fgets
、fputs
等)来处理文本文件是很方便的。这些函数可以直接读取和写入文本文件中的文本数据,并且适合于处理文本内容。
2.3 二进制文件
二进制文件以字节序列的形式存储数据,而不是以人类可读的字符形式。这意味着它们可以存储任何类型的数据,包括数字、图像、音频、视频等。由于二进制文件不以人类可读的形式存储数据,它们无法直接用文本编辑器打开和阅读。
在C语言中,处理二进制文件需要使用fread
和fwrite
等函数,这些函数可以直接读取和写入二进制数据。因为二进制文件不关心数据的具体内容,所以它们非常适合用于存储和传输各种类型的数据。
2.4 区别
- 存储方式:文本文件以字符形式存储数据,而二进制文件以字节序列的形式存储数据。
- 可读性:文本文件可以被文本编辑器直接打开和阅读,而二进制文件不具备可读性。
- 用途:文本文件适合存储文本内容,而二进制文件适合存储各种类型的数据。
2.5 数据在文件中的存储方式
字符型的数据以ASCII形式进行存储
数值型数据既可以使用ASCII形式进行存储,也可以使用二进制形式存储
操作示例:
操作环境:VS2022
①创建txt文件
首先在工程文件夹中创建一个txt文件test。
② 运行代码并在VS中打开二进制文件
#include <stdio.h> int main() { int a = 100; FILE* pf = fopen("test.txt", "wb"); fwrite(&a, 4, 1, pf);//⼆进制的形式写到⽂件中 fclose(pf); pf = NULL; return 0; }
当运行后打开txt文件后发现了如下图所示的显示:
出现此种情况的原因是因为没有打开二进制文本模式,所以看到的是我们看不懂的乱码
打开二进制文件方法
通过以下操作即可实现打开二进制文件
此时再打开txt文件查看:
为什么是这个结果呢?
这时候就涉及到了上文所讲述的数据在文件中的存储方式,将内容简化为文字就是下图所示(以10000为例):
当放入10000时实际上放入的是10011100010000这个二进制序列,在二进制文件中显示的是十六进制的数字序列,所以存进去的时候看的是十六进制大小2710,由于是VS是小段存储,所以实际存储进去的序列的就是1027(拿出来的时候还是2710)。
以上就是关于运行代码在VS中的二进制文件中的相关知识。
3. 文件的打开和关闭
3.1 流和标准流
3.1.1 流
流的概念
流提供了一个统一的接口,使得程序可以以统一的方式处理来自不同数据源和输出到不同目标的数据。通过使用标准库中提供的函数,比如fopen
、fclose
、fread
、fwrite
等,可以操作流并对数据进行读写操作。
在C语言中,流是以FILE
结构表示的,每个流都对应着一个FILE
类型的指针。通过这个指针,程序可以访问流的属性和进行读写操作。标准输入流、标准输出流和标准错误流在程序启动时就已经自动打开,无需额外操作。
我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出操作各不相同,为了⽅便程序员对各种设备进行方便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。
3.1.2 标准流
在C语言中,标准库提供了一组预定义的流,称为标准流(standard streams),它们包括标准输入流(stdin)、标准输出流(stdout)和标准错误流(stderr)。
- 标准输入流(stdin):标准输入流用于从程序外部读取数据,通常与键盘输入相关联。当你使用
scanf
等函数读取用户输入时,实际上是从标准输入流中读取数据。- 标准输出流(stdout):标准输出流用于向程序外部输出数据,通常与屏幕输出相关联。当你使用
printf
等函数输出信息时,实际上是往标准输出流中写入数据。- 标准错误流(stderr):标准错误流用于输出程序的错误信息,通常与屏幕输出相关联。当程序发生错误时,可以使用
fprintf(stderr, ...)
函数将错误信息输出到标准错误流。
3.2文件指针
每个被使用的文件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如问件的名字,文件状态及文件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名FILE。
stdio.h 头⽂件中有以下的⽂件类型申明:
struct _iobuf { char* _ptr; int _cnt; char* _base; int _flag; int _file; int _charbuf; int _bufsiz; char* _tmpfname; }; typedef struct _iobuf FILE;
当我们打开一个文件的时候系统会自己创建该结构,⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量。
FILE*类型的指针变量的创建:
FILE* pf;
文件指针变量实际上并不直接指向文件本身,而是指向文件在内存中的相关信息。通过文件指针变量,程序可以间接地找到与其关联的文件,即通过文件指针变量所指向的信息来操作对应的文件。
当我们使用fopen
函数打开一个文件时,该函数会返回一个指向FILE
类型的指针,这个指针指向了文件在内存中的相关信息,比如文件的状态、位置等。通过这个文件指针,程序可以对文件进行读取、写入等操作,实现了对文件的间接访问和控制。
因此,虽然文件指针变量本身并不直接指向文件的实际内容,但通过它可以间接地找到与其关联的文件,并对文件进行操作。这种间接性的设计使得程序能够更加灵活地处理文件,同时也增强了程序与文件之间的抽象层级。
3.3 文件的打开和关闭
文件在读写时需要先打开文件,使用后关闭文件。
- fopen 函数来打开⽂件
- fclose 来关闭⽂件
//打开⽂件 FILE* fopen(const char* filename, const char* mode); //关闭⽂件 int fclose(FILE* stream);
mode表示打开文件的方式,有以下方式:
代码示例:
/* fopen fclose example */ #include <stdio.h> int main() { FILE* pFile; //打开⽂件 pFile = fopen("myfile.txt", "w"); //⽂件操作 if (pFile != NULL) { fputs("fopen example", pFile); //关闭⽂件 fclose(pFile); } return 0; }
4.文件读写函数
4.1 顺序读写
fgetc | 字符输⼊函数 | 所有输⼊流 |
fputc | 字符输出函数 | 所有输出流 |
fgets | ⽂本⾏输⼊函数 | 所有输⼊流 |
fputs | ⽂本⾏输出函数 | 所有输出流 |
fscanf | 格式化输⼊函数 | 所有输⼊流 |
fprintf | 格式化输出函数 | 所有输出流 |
fread | ⼆进制输⼊ | ⽂件 |
fwrite | ⼆进制输出 | ⽂件 |
4.2 随机读写
fseek | int fseek ( FILE * stream, long int offset, int origin ); |
ftell | long int ftell ( FILE * stream ); |
rewind | void rewind ( FILE * stream ); |
详见课件