1.文件的基础认知
在讲解文件的基础认知之前,首先让我们了解一下什么是文件:
文件:磁盘(硬盘)上的文件就是文件,它是在计算机中以实现某种功能、或某个软件的部分功能为目的而定义的一个单位。
(1)文件的分类
了解了什么是文件之后,那么文件有哪些分类呢?一般来讲文件分为两种:
1.程序文件:程序文件包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)
2.数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件
(2)文件名
像人一样,每一个文件都有其名称,即文件名:
(3)二进制文件和文本文件
根据数据的组织形式,数据文件通常被分为了文本文件或者二进制文件。
二进制文件:在储存到外存是将二进制数据不进行转换,数据在内存中以二进制的形式存储就是二进制文件
文本文件:在储存到外存是将二进制数据进行转换,以ASCII字符的形式存储的文件就是文本文件
例如:
用一句话分别是二进制文件还是文本文件:就是看其储存到外存中时有没有进行转换。
(4)文件指针
在介绍文件指针之前,让我们先了解一下什么是文件信息区:
文件信息区:当我们在使用文件的时候,编译器都会创建一个其对应的文件信息区,它是用来存放文件的相关信息的(如文件的名字,文件状态及文件当前的位置等),这些信息是保存在一个结构体变量中(该结构体类型是由系统声明的),并且使用typedef重命名为FILE
这是Visual Studio中的文件信息区:
struct _iobuf { char *_ptr; int _cnt; char *_base; int _flag; int _file; int _charbuf; int _bufsiz; char *_tmpfname; }; typedef struct _iobuf FILE;
注:不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。
了解了什么是文件信息区之后,我们在讲解一下文件指针,文件指针,顾名思义就是存放文件地址的指针,每当我们打开一个文件的时候,它就会返回文件信息区的指针,这时我们就可以使用文件指针接收它,并通过文件指针间接的操作与它关联的文件。
以上我们就初步了解了有关文件的基础认知。
2.文件的操作流程
操作文件大致分为三步:打开文件、操作文件、关闭文件
(1)打开文件
我们可以使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件,该函数会返回一个文件指针:
该函数的第一个参数是你想要打开的文件的文件名(例如:“ c:\code\test.txt ”),第二个参数为你想要对该文件进行的操作,大致的操作方式有如下几种:
文件使用方式 | 含义 | 如果指定文件不存在 |
“r”(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
“w”(只写 | 为了输出数据,打开一个文本文件 | 建立一个新的文件 |
“a”(追加) | 向文本文件尾添加数据 | 建立一个新的文件 |
rb”(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
“wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
“ab”(追加) |
向一个二进制文件尾添加数据 | 出错 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“w+”(读写) | 为了读和写,建议一个新的文件 | 建立一个新的文件 |
“a+”(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
“rb+”(读写) | 为了读和写打开一个二进制文件 | 出错 |
“wb+”(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
“a+”(读写) | 打开一个二进制文件,在文件尾进行读写 | 建立一个新的文件 |
注意:文件的使用方式是使用双引号(“ ”),而不是单引号!!!
现在我们使用实例来使你更好的进行理解:
#include<stdio.h> int main() { //创建一个文件名为text.txt的文件 FILE* p = fopen("text.txt", "w"); //防止打开失败返回空指针 if (p == NULL) { perror("fopen"); return 1; } return 0; }
注:当我们打开 / 创建一个文件之后都要对文件指针进行判断,判断其是否为NULL指针,如果为NULL指针,就没有必要进行下面的操作了,就直接退出程序即可。
上面的代码中我对每一步进行了讲解,使你能更好的理解如何打开文件。
以上我们就完成了文件的打开 / 创建。
(2)关闭文件
在对文件操作完成之后,就要进行关闭的操作,为了关闭文件,我们就要使用 fclose( ) 函数。函数的原型如下:
该函数的参数为你要关闭的文件的文件指针(FILE类型指针),我们直接使用实例来深入理解。
继续对上面的代码进行关闭操作:
#include<stdio.h> int main() { //创建一个文件名为text.txt的文件 FILE* p = fopen("text.txt", "w"); //防止打开失败返回空指针 if (p == NULL) { perror("fopen"); return 1; } //关闭文件 fclose(p); p = NULL; return 0; }
注意:当我们关闭文件的时候,由于该文件已被关闭,那么FILE类型的文件指针变为了野指针,所以这时我们要将文件指针置为NULL。
以上我们就完成了文件的关闭。
(3)文件的读取和输入
现在我们已经了解了文件如何创建 / 打开和关闭,那么操作文件的最重要的部分——读取和输入该如何操作呢?
首先我们要知道操作文件的几个函数:
函数名 | 功能 | 适用性 |
fgetc() | 字符输入函数 | 所有输入流 |
fputc() | 字符输出函数 | 所有输出流 |
fgets() | 文本行输入函数 | 所有输入流 |
fputs() | 文本行输出函数 | 所有输入流 |
先大体看一下有哪些函数,下面会进行详细讲解!
3.实例:操作文件
(1)fputc函数
先看一下官网对其的解释:
该函数的第一个参数是你想存入文件的字符,第二个参数是该文件的文件指针,返回值为存入字符的ASCII码值。
我们直接使用实例来加深对其的理解:
#include<stdio.h> int main() { FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("fopen"); return 1; } //将字符Z存入文件名为test.txt的文件 fputc('Z', pf); fclose(pf); pf = NULL; return 0; }
这样我们就将字符Z存入了文件。
(2)fgetc函数
先看一下官网对其的解释:
该函数的参数为要读取的文件的文件指针,返回值为为读取的字符的ASCII码值。
注:当读取失败或者读取到文件末尾时会返回EOF(本质是-1)
我们直接使用实例来加深对其的理解:
我们继续使用上面例子中的文件(其中我们存放了字符 Z )
#include<stdio.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //读取文件名为test.txt的文件中的字符 int ch = fgetc(pf); printf("%c", ch); fclose(pf); pf = NULL; return 0; }
这样我们就获取了文件中的字符。
补充:
我们可以使用fgetc函数将文件中的字符一个一个的获取出来,达到获取文件中所有内容的目的。
#include<stdio.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //获取文件中的所有内容 int ch = 0; while (((ch = fgetc(pf)) != EOF)) { printf("%c", ch); } fclose(pf); pf = NULL; return 0; }
我们知道当读取失败或者读取到文件末尾时才会返回EOF,所有我们使用EOF作为判断依据循环获取字符即可,这样我们就可以获取文件中所有内容了。
(3)fputs函数
先看一下官网对其的解释:
该函数的第一个参数为一个字符指针,即你可以将想写入文件的字符串的首地址传入,第二个参数为你想写入的文件的文件指针。
我们直接使用实例来加深对其的理解:
#include<stdio.h> int main() { FILE* pf = fopen("test.txt","w"); if (pf == NULL) { perror("fopen"); return 1; } //该字符串为要写入的字符串 char str[] = "hello world"; fputs(str, pf); fclose(pf); pf = NULL; return 0; }
这样我们就将字符串成功写入文件中了。
(4)fgets函数
先看一下官网对其的解释:
该函数的第一个参数为一个字符指针,这里你要填入的是一个容器地址,该容器用来存放读取出的字符串,第二个参数为要读取的字符串的长度,第三个参数是想要读取字符串的文件的文件指针,该函数的返回值为一个字符指针。
注:当读取成功时,返回的为读取到的字符串的地址,如果读取失败或者读取到文件末尾,则返回NULL指针。
我们直接使用实例来加深对其的理解:
#include<stdio.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //存放返回字符串的容器 char str[20]; //读取数据 fgets(str, 10, pf); printf("%s", str); fclose(pf); pf = NULL; return 0; }
从结果中我们发现了一个问题——我想让它读取10个字符,但是为什么它只读取了9个呢?其实该函数就是会读取你所想让它读取的字符长度 -1 个字符,因为该函数会留一个字符用来放入‘ \0 ’。
这样我们就完成了从文件中读取字符串的任务。
补充:
我们也可以使用该函数来读取文件中的所有内容。
#include<stdio.h> int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("fopen"); return 1; } //存放返回字符串的容器 char str[20]; //读取文件中的所有数据 while (fgets(str, 10, pf) != NULL) { printf("%s", str); } fclose(pf); pf = NULL; return 0; }
这里我们使用其如果读取失败或者读取到文件末尾才返回NULL指针的特性完成了读取文件所有内容的操作。