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);//a
ch = fgetc(pf);
printf("%c\n", ch);//b
ch = fgetc(pf);
printf("%c\n", ch);//c
ch = fgetc(pf);
printf("%c\n", ch);//d
ch = fgetc(pf);
printf("%c\n", ch);//e
int 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);//a
ch = fgetc(pf);
printf("%c\n", ch);//b
ch = fgetc(pf);
printf("%c\n", ch);//c
ch = fgetc(pf);
printf("%c\n", ch);//d
ch = fgetc(pf);
printf("%c\n", ch);//e
rewind(pf);
ch = fgetc(pf);
printf("%c\n", ch);//a
fclose(pf);
pf = NULL;
return 0;
}
这个函数可以让文件指针重置到起始位置。