我们为了让写的程序储存在电脑中,就可以使用文件。这样就不会随着程序退出,内存回收造成数据丢失了。
文章目录
- 一、文件
- 二、二进制文件和文本文件
- 三、文件的打开和关闭
- 四、文件的顺序读写
-
- [4.1 fgetc](#4.1 fgetc)
- [4.2 fputc](#4.2 fputc)
- [4.3 fgets](#4.3 fgets)
- [4.4 fputs](#4.4 fputs)
- [4.5 fscanf](#4.5 fscanf)
- [4.6 fprintf](#4.6 fprintf)
- [4.7 fread](#4.7 fread)
- [4.8 fwrite](#4.8 fwrite)
- [4.9 对比一组函数](#4.9 对比一组函数)
-
- [4.9.1 sscanf和sprintf](#4.9.1 sscanf和sprintf)
- 五、文件的随机读写
-
- [5.1 fseek](#5.1 fseek)
- [5.2 ftell](#5.2 ftell)
- [5.3 rewind](#5.3 rewind)
- 六、文件读取结束的判定
-
- [6.1 被错误使用的feof](#6.1 被错误使用的feof)
- 七、文件缓冲区
一、文件
如果没有⽂件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失
了,等再次运⾏程序,是看不到上次程序的数据的,如果要将数据进⾏持久化的保存,我们可以使⽤
⽂件。
1.1什么是文件
磁盘(硬盘)上的⽂件是⽂件。但是在程序设计中,我们⼀般谈的⽂件有两种:程序⽂件 、数据⽂件 (从⽂件功能的⻆度来分类
的)。
1.2程序文件
程序⽂件包括源程序⽂件(后缀为.c),⽬标⽂件(windows环境后缀为.obj),可执⾏程序(windows环境后缀为.exe)。
1.3数据文件
⽂件的内容不⼀定是程序,⽽是程序运⾏时读写的数据,⽐如程序运⾏需要从中读取数据的⽂件,或者输出内容的⽂件。
在以前各章所处理数据的输⼊输出都是以终端为对象的,即从终端的键盘输⼊数据,运⾏结果显⽰到显⽰器上。
其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使⽤,这⾥处理的就是磁盘上⽂件。
1.4文件名
⼀个⽂件要有⼀个唯⼀的⽂件标识,以便⽤⼾识别和引⽤。
⽂件名包含3部分:⽂件路径+⽂件名主⼲+⽂件后缀
例如: c:\code\test.txt
为了⽅便起⻅,⽂件标识常被称为⽂件名。
二、二进制文件和文本文件
根据数据的组织形式,数据⽂件被称为⽂本⽂件或者⼆进制⽂件。
数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的⽂件中,就是⼆进制⽂件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是⽂
本⽂件。
⼀个数据在⽂件中是怎么存储的呢?
字符⼀律以ASCII形式存储,数值型数据既可以⽤ASCII形式存储,也可以使⽤⼆进制形式存储。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽
⼆进制形式输出,则在磁盘上只占4个字节(VS2019测试)。
c
int main()
{
int a = 10000;
FILE* pa = fopen("text.txt", "wb");
if (pa = NULL)
{
perror("fopen");
return 1;
}
fwrite(&a, 4, 1, pa);//以二进制都形式写到文件中
fclose(pa);
pa = NULL;
return 0;
}
三、文件的打开和关闭
3.1流和标准流
3.1.1流
我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。
C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。
⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。
3.1.2标准流
那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语⾔程序在启动的时候,默认打开了3个流:
- stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。
- stdout - 标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出流中。
- stderr - 标准错误流,⼤多数环境中输出到显⽰器界⾯。
这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。
stdin、stdout、stderr 三个流的类型是:FILE *,通常称为文件指针 。
C语⾔中,就是通过 FILE* 的⽂件指针来维护流的各种操作的。
3.2文件指针
缓冲文件系统中,关键的概念是"文件类型指针 ",简称"文件指针"。
每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名 FILE.
例如,VS2013 编译环境提供的 stdio.h 头⽂件中有以下的⽂件类型申明:
c
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结构的变量,这样使⽤起来更加⽅便。
c
FILE* pa;
定义pa是⼀个指向FILE类型数据的指针变量。可以使pf指向某个⽂件的⽂件信息区(是⼀个结构体变量)。通过该⽂件信息区中的信息就能够访问该⽂件。也就是说,通过⽂件指针变量能够间接找到与它关联的⽂件。
3.3文件的打开和关闭
⽂件在读写之前应该先打开⽂件,在使⽤结束之后应该关闭⽂件。
在编写程序的时候,在打开⽂件的同时,都会返回⼀个FILE*的指针变量指向该⽂件,也相当于建⽴了指针和⽂件的关系。
ANSI C 规定使⽤ fopen 函数来打开⽂件, fclose 来关闭⽂件。
c
//打开文件
FILE* fopen(const char* filename, const char* mode);
//关闭文件
int fclose(FILE* stream);
mode表示文件的打开模式,下面都是文件的打开方式。
四、文件的顺序读写
4.1 fgetc
fgetc 的功能是字符输入函数,适用于所有输入流。
4.2 fputc
fputc 的功能是字符输出函数,适用于所有输出流。

4.3 fgets
fgets 的功能是字符输入函数,适用于所有输入流。
4.4 fputs
fputs 的功能是字符输出函数,适用于所有输出流。
4.5 fscanf
fscanf的功能是格式化输入函数,适用于所有的输入流。
4.6 fprintf
fprintf 的功能是格式化输出函数,适用于所有的输出流。
4.7 fread
fread的功能是二进制输入,适用于文件输入流。

4.8 fwrite
fwrite的功能是二进制输出,适用于文件输出流。

4.9 对比一组函数
scanf、fscanf、sscanf、
printf、fprintf、sprintf、
scanf和printf我们很熟悉,scanf是从标准输入流上读取格式化的数据,printf是把数据以格式化的形式打印在标准输出流上。
fscanf和fprintf的实现形式在上面写过。fscanf是从指定的输入流上读取格式化的数据。fprintf是把数据以格式化的形式打印在指定的输出流上。
4.9.1 sscanf和sprintf

五、文件的随机读写
5.1 fseek
c
int fseek ( FILE * stream, long int offset, int origin )
根据文件指针的位置和偏移量来定位文件指针(文件内容的光标)。

要注意,在这个函数的参数中有是三个常量。
- SEEK_SET是文件指针的起始位置。
- SEEK_CUR是文件指针的当前位置。
- SEEK_END文件指针处于末尾位置。
5.2 ftell
返回文件指针相对于起始位置的偏移量
c
long int ftell ( FILE * stream );

ftell函数表示的是文件指针所处位置先对于文件指针起始位置的偏移量。
5.3 rewind
让文件指针的位置回到文件的起始位置。
c
void rewind ( FILE * stream );

这个代码,在用rewind函数之前文件指针在f的位置,偏移量为6。通过用rewind函数返回了文件的起始位置,偏移量变为0。
六、文件读取结束的判定
6.1 被错误使用的feof
牢记:在⽂件读取过程中,不能⽤feof函数的返回值直接来判断⽂件的是否结束。
feof 的作⽤是:当⽂件读取结束的时候,判断是读取结束的原因是否是:遇到⽂件尾结束。
-
⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
fgetc 判断是否为 EOF .
fgets 判断返回值是否为 NULL
-
⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。
例如:
fread判断返回值是否⼩于实际要读的个数。
七、文件缓冲区
ANSIC 标准采⽤"缓冲⽂件系统" 处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为程序中每⼀个正在使⽤的⽂件开辟⼀块"⽂件缓冲区"。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的⼤⼩根据C编译系统决定的。