1、为什么使用文件?
我们前面学习结构体,在写通讯录的时候会发现一个问题,我们向通讯录里面录入数据,当程序退出的时候,记录的数据也随之没有了,等下次我们在再调用通讯录时,又得重新录入数据,那也太不方便了。
所以我们就应该弄出来一个可以保存信息的通讯录,将录入的数据存在磁盘文件里,这样我们才真正的做到了数据的持久化。
2、什么是文件?
磁盘上的文件是文件
但是在程序设计中,我们一般谈及的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)
2.1程序文件
包括源文件程序(后缀是.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。
2.2数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行时需要从中读取数据的文件,或者输出内容的文件。
本章讨论的是数据文件,在此之前我们接触到的C语言可能都是以终端的键盘为输入输出的对象,而能进行输入输出的不仅仅有终端,我们也可以从文件中读取数据,也可以将内容输出到文件中。
2.3文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件名包含三个部分:文件路径+文件主干+文件后缀
例如:c:\code\test.txt
为了方便起见,通常将文件标识称为文件名。
3、文件的打开和关闭
3.1文件指针
每一个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字、文件状态以及文件的位置等)。这些信息是保存在一个结构体变量中的。该结构体是由系统声明的,取名为FILE。
每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必要在意其中的细节。
而通过文件指针变量对其进行管理会更加方便。
下面我们定义一个文件指针:
FILE* pf;//文件指针变量
我们只需要在程序中调用这个文件指针变量,就可以找到相应的文件。
3.2文件的打开和关闭
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件。
那么具体有哪些打开方式呢?下面有五个重要常用的打开方式:
文件使用方式 | 含义 | 如果指定文件不存在 |
---|---|---|
"r"(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
"w"(只写) | 为了输出数据,打开一个一个文本文件 | 建立一个新的文件 |
"a"(追加) | 向文本文件尾添加数据 | 建立一个新的文件 |
"rb"(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
"wb"(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
当然fopen函数也是有可能被打开失败的,当打开失败的话,将会返回一个空指针
那么实践中,代码是如何具体实现的呢?
#include <stdio.h>int main(){FILE* pFile;//打开文件pFile = fopen("myfile.txt", "w");if (pFile == NULL) {perror("fopen");return 1;}/*写文件....*///关闭文件fclose(pFile);pFile = NULL;return 0;}
4、文件的顺序读写
顺序读写函数介绍
功能 | 函数名 | 适用于 |
---|---|---|
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输出流 |
文本行输入函数 | fgets | 所有输入流 |
文本行输出函数 | fputs | 所有输出流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprintf | 所有输出流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
这是一组对文件进行读写字符的函数:
/* fopen fclose example */ #include <stdio.h>int main(){FILE* pFile;//打开文件pFile = fopen("myfile.txt", "r");if (pFile == NULL) {perror("fopen");return 1;}fputc('a', pFile);fputc('b', pFile);fputc('c', pFile);fputc('d', pFile);int ch = fgetc(pFile);printf("%c ", ch);ch = fgetc(pFile);printf("%c ", ch);ch = fgetc(pFile);printf("%c ", ch);ch = fgetc(pFile);printf("%c ", ch);fclose(pFile);pFile = NULL;return 0;}
我们可以看到我们在进行,写文件的操作时,确实在文件myfile.txt里写入了abcd,然后我们再进行读文件的操作时,也可以在终端上看到abcd,所以这就是读写文件操作和结果。
注:fgetc在读取文件时,成功则返回相应的字符,失败的话返回的是EOF
所以我们读文件也可以这样:
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h>/* fopen fclose example */ #include <stdio.h>int main(){FILE* pFile;//打开文件pFile = fopen("myfile.txt", "r");if (pFile == NULL) {perror("fopen");return 1;}fputc('a', pFile);fputc('b', pFile);fputc('c', pFile);fputc('d', pFile);//读取int ch = 0;while ((ch = fgetc(pFile)) != EOF){printf("%c ", ch);}fclose(pFile);pFile = NULL;return 0;}
int main() {FILE* pf = fopen("data.txt", "w");if (pf == NULL){perror("fopen");return 1;}//写文件//fputs("hello", pf);char arr[] = "hello";fputs(arr, pf);fputs("world", pf);fclose(pf);pf = NULL;return 0; }
int main() {FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen");return 1;}//读文件char arr[100] = {0};fgets(arr, 100, pf);printf("%s", arr);fgets(arr, 100, pf);printf("%s", arr);fclose(pf);pf = NULL;return 0; }
这一组函数的功能是可以对字符串进行读写操作。
#include<stdio.h> struct S {float f;char c;int n; };int main() {struct S s = { 3.14f, 'w', 100 };FILE* pf = fopen("data.txt", "w");if (pf == NULL){perror("fopen");return 1;}//写文件fprintf(pf, "%f-%c-%d", s.f, s.c, s.n);fclose(pf);pf = NULL;return 0; }
#include<stdio.h> struct S {float f;char c;int n; };int main() {struct S s = {0};FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen");return 1;}//读文件fscanf(pf, "%f-%c-%d", &(s.f), &(s.c), &(s.n));printf("%f-%c-%d\n", s.f, s.c, s.n);fclose(pf);pf = NULL;return 0; }
这一组函数功能可以将带有特殊格式的进行读写操作。
#include<stdio.h> //二进制的方式写进文件 int main() {int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//写文件FILE*pf = fopen("data.txt", "wb");if (pf == NULL){perror("fopen");return 1;}//二进制的写文件fwrite(arr, sizeof(arr[0]), sizeof(arr)/sizeof(arr[0]), pf);fclose(pf);pf = NULL;return 0; }
#include<stdio.h> //二进制的方式读取文件 int main() {int arr[10] = {0};//写文件FILE* pf = fopen("data.txt", "rb");if (pf == NULL){perror("fopen");return 1;}//二进制的读文件fread(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr[i]);}fclose(pf);pf = NULL;return 0; }
这一组是以二进制的形式进行读写文件的
5、文件的随机读写
5.1fseek
5.2ftell
#include<stdio.h>
int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL) {perror("fopen");return 1;}int ch = fgetc(pf);printf("%c\n", ch);//ach = fgetc(pf);printf("%c\n", ch);//bch = fgetc(pf);printf("%c\n", ch);//cch = fgetc(pf);printf("%c\n", ch);//dch = fgetc(pf);printf("%c\n", ch);//eint pos = ftell(pf);printf("pos = %d\n", pos);fclose(pf);pf = NULL;return 0;
}
这个函数就是可以查看,当前文件指针指向了什么地方,从起始位置开始的偏移量。
5.3rewind
#include<stdio.h>
int main()
{FILE* pf = fopen("data.txt", "r");if (pf == NULL) {perror("fopen");return 1;}int ch = fgetc(pf);printf("%c\n", ch);//ach = fgetc(pf);printf("%c\n", ch);//bch = fgetc(pf);printf("%c\n", ch);//cch = fgetc(pf);printf("%c\n", ch);//dch = fgetc(pf);printf("%c\n", ch);//erewind(pf);ch = fgetc(pf);printf("%c\n", ch);//afclose(pf);pf = NULL;return 0;
}
这个函数可以让文件指针重置到起始位置。