1.为什么使用文件
很简单 长久的存储数据
如果没有文件,我们写程序所产生的数据是存储在电脑的内存中的,一但程序结束或退出,内存就会回收,数据就丢失了,等我们再次运行程序的时候,是找不到上次程序产生的数据的,如果末尾想要将数据进行长期的保存,那就可以使用文件。
2.文件是什么?
磁盘(硬盘)上的文件就是文件。
但是在程序设计中,我们说的文件有两种:程序文件、数据文件(是从文件功能的角度来分类的)。
2.1程序文件
程序文件包含源程序文件(xxx.c),目标文件(在Windows环境下为xxx.obj),可执行程序(在Windows环境下为xxx.exe).
2.2数据文件
该类型文件的内容就不是程序,而是程序运行时所存入的数据,例如程序运行需要读取数据的文件,或输出产生并输出内容的文件。
该文章讨论的是数据函数
3.二进制文件和文本文件
根据数据的存储模式,数据文件由分为文本文件和二进制文件。
数据是以二进制的形式在存储在内存中的,如果不转换的输出或者存储在文件中,就是二进制文件。
如果在输出上要求以ASCII码的形式存储,则需要在存储前进行转换。以ASCII字符的形式存储文件就是文件文件。
那么一个数据是怎么存储在文件上的呢?
字符一律按照ASCII码的形式存储,数值形式的数据既可以用ASCII码形式存储,也可以用二进制的形式存储。
假设有个整数10000,如果以ASCII码的形式存储,就会占用磁盘中的5个字节,如果是以二进制存储,则只占用磁盘中的4个字节。
4.文件的打开和关闭
4.1流和标准流
流
我们在程序的数据需要输出到各种外部设备,也需要从各种外部设备中获取数据,不同设备的输入输出操作各不相同。
为了方便程序员对各种设备进行操作,我们就抽象出了流的概念,我们可以将流想象成流淌着数据的河。
C程序针对文件、画面、键盘等数据的输入输出都是通过流操作的。
一般情况下,我们想往流里写数据,或者往流里读取数据,都是要打开流,然后再进行操作。
标准流
那为什么我们从键盘输入数据、向屏幕输出数据时,并没有打开流这个操作呢?
那是因为C语言程序启动时,会自动打开三个流:
- 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;
但VS2022编译环境中已经不一样了:
typedef struct _iobuf
{
void* _Placeholder;
} FILE;
不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。
每当打开一个文件时,系统就会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,我们使用者并不需要关心其中的细节。
创建FILE*类型的指针变量:
FILE* pf;//文件指针变量
将pf定义为一个FILE类型的指针变量,可以使pf指向某个文件的文件信息区(使一个结构体变量),通过该文件信息区中的信息就可以访问该文件。
也就是说,我们可以通过文件指针变量来间接找到与它关联的文件。
如图:
4.3文件的打开和关闭
文件在读写之前要打开文件,使用结束后应该关闭文件(常识)。
在编写程序时,打开文件的同时会返回一个FILE*类型的指针,这也相当于建立了指针和文件的关系。
ANSI C规定了使用fopen函数来打开文件,使用fclose来关闭文件。
//打开文件
FILE *fopen( const char *filename, const char *mode );
//关闭文件
int fclose( FILE *stream );
如果fopen
函数打开文件失败,会返回NULL
mode表示文文件的打开方式:
文件使用方式 | 含义 | 如果指定文件不存在 |
---|---|---|
“r”(只读) | 为了输⼊数据,打开⼀个已经存在的⽂本⽂件 | 出错 |
“w”(只写) | 为了输出数据,打开⼀个⽂本⽂件(每一次打开都会清空文件内容) | 建⽴⼀个新的⽂件 |
“a”(追加) | 向⽂本⽂件尾添加数据 | 建⽴⼀个新的⽂件 |
“rb”(只读) | 为了输⼊数据,打开⼀个⼆进制⽂件 | 出错 |
“wb”(只写) | 为了输出数据,打开⼀个⼆进制⽂件(每一次打开都会清空文件内容) | 建⽴⼀个新的⽂件 |
“ab”(追加) | 向⼀个⼆进制⽂件尾添加数据 | 建⽴⼀个新的⽂件 |
“r+(读写)” | 为了读和写,打开⼀个⽂本⽂件 | 出错 |
“w+(读写)” | 为了读和写,打开⼀个⽂本⽂件(每一次打开都会清空文件内容) | 建⽴⼀个新的⽂件 |
“a+(读写)” | 打开⼀个⽂件,在⽂件尾进⾏读和写 | 建⽴⼀个新的⽂件 |
“rb+(读写)” | 为了读和写打开⼀个⼆进制⽂件 | 出错 |
“wb+(读写)” | 为了读和写打开⼀个⼆进制⽂件(每一次打开都会清空文件内容) | 建⽴⼀个新的⽂件 |
“ab+(读写)” | 打开⼀个⼆进制⽂件,在⽂件尾进⾏读和写 | 建⽴⼀个新的⽂件 |
实际代码:
int main()
{
//打开文件
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)
{
perror("fopen");
exit(1);//该函数的头文件为<stdlib.h>
}
//操作文件
fputs("fopen example", pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
运行前:
运行后:
结语
最后感谢您能阅读完此片文章,如果有任何建议或纠正欢迎在评论区留言。如果您认为这篇文章对您有所收获,点一个小小的赞就是我创作的巨大动力,谢谢!!!