使用文件的意义
前面我们写过很多的代码,运行了很多的程序,但是一旦程序结束了或者断电了,这数据就会不见了,因为这些数据是存储在内存中的,但是使用文件可以有效防止
文件是存在硬盘的
什么是文件
在程序设计中,我们一般谈论两种:一种是程序文件,另一种是数据文件
程序文件
包含有源程序文件(如.c)、目标文件(.obj)、 可执行文件(.exe)
数据文件
数据文件就是被读取数据或者写入数据的文件,或者我们可以理解为我们操作哪个文件哪个文件就是数据文件
操作对象
前面我们使用scanf和printf所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显
示器上。
现在我们把键盘和屏幕换成了文件,输入就是读取文件内容, 输出就是往文件里面写入数据,
这里的程序中的数据是保存在内存中的
文件名
一个文件要用一个唯一的文件标识符,以便用户识别和引用
文件名包含三部分:文件路径+文件名主干+文件后缀
例如: C:\a\b.c
C:\a\是文件路径 ,b是文件主干 .c是文件后缀
文件的打开和关闭
文件指针
C语言中每个文件的使用都会在内存开辟一块空间,也就是文件信息区,用来存放文件的相关信息,这些信息是保存在一个名叫FILE类型的结构体变量中,这个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;
不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。
每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,
使用者不必关心细节。
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便
那我们可以创建一个FILE*类型的指针来接收这个FILE类型的结构体变量的地址
FILE* pf;//文件指针变量
定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件
那我们怎么找到这个变量的地址呢?,当我们打开一个文件的时候就会创建一个文件信息区,并且自动填充相关信息
文件的打开和关闭
打开
fopen 函数返回一个指向 FILE 结构体的指针,该结构体用于表示打开的文件。如果打开文件失败,函数将返回 NULL。在使用完打开的文件后,应该使用 fclose 函数关闭文件
filename:文件名
mode:打开方式
这两个参数只需我们传入两个字符串常量就可以,传入字符串常量就会把第一个字符的地址传入进去
打开方式如下:
关闭
需要注意的是,fclose 函数将文件缓冲区中的数据写入磁盘,并释放与该文件相关的所有资源。在关闭文件之前,应该确保已完成对文件的所有操作。
如果流成功关闭,则返回零值。
失败时,将返回 EOF。
#include<stdio.h> int main() { FILE *p = fopen("./test.txt", "w"); if (p == NULL) { perror("fopen"); return 1; } int b = fclose(p); printf("%d", b); p = NULL; return 0; }
文件的顺序读写
前面我们使用scanf和printf是在终端操作的,如果我们要在文件上操作就不能使用这些函数
我们要站在内存的角度思考,写入内存的数据叫输入,从内存中输出数据叫输出
这个图是一个简便的,文件到程序和文件到程序都不是一步到位的,都要经过一个流,
本质上我们是操作流来操作文件,我们使用scanf和printf可以直接操作数据,是因为,C语言程序只要运行起来就会打开三个流 :标准输入流(stdin) 、标准输出流(stdout)、 标准错误流(stderr)
stdin、stdout 、stderr的类型都是FILE*类型的
输出流(把数据写入到流,而不是写入到文件)(对象是屏幕):
#include<stdio.h> #include<stdlib.h> int main() { //打开文件 FILE* file = fopen("./test.txt", "w"); if (file == NULL) { perror("fopen"); return 1; } char str; int a = 0; while ( a < 10) { scanf("%c", &str); getchar(); fputc(str, stdout); a++; } fclose(file); file = NULL; return 0; }
输入流(从流中获取数据,而不是从文件里面获取)(对象是键盘):
#include<stdio.h> #include<stdlib.h> int main() { //打开文件 FILE* file = fopen("./test.txt", "w"); if (file == NULL) { perror("fopen"); return 1; } char str; str = fgetc(stdin); printf("%c", str); fclose(file); file = NULL; return 0; }
文件流:文件变量的地址,也就是fopen返回的地址
字符输出函数
fputc 函数将字符写入文件后,文件指针会自动向后移动一个字符位置。如果写入成功,函数将返回写入的字符;否则,返回 EOF。
#include<stdio.h> #include<stdlib.h> int main() { //打开文件 FILE* file = fopen("./test.txt", "w"); if (file == NULL) { perror("fopen"); return 1; } char str; int a = 0; while ( a < 10) { scanf("%c", &str); getchar(); fputc(str, file); a++; } fclose(file); file = NULL; return 0; }
字符输入函数
stream是一个指向文件的指针,指向我们要读取的文件流。函数返回值是读取的字符的ASCII码值,如果到达文件末尾或者读取出错,返回EOF。
#include<stdio.h> #include<stdlib.h> int main() { FILE* file = fopen("./test.txt", "r"); if (file == NULL) { perror("fopen"); return 1; } char a; int b = 0; while (b < 5) { a = fgetc(file); printf("%c ", a); b++; } fclose(file); file = NULL; return 0; }
文本字符串输出函数
,str是要写入的字符串,stream是一个指向文件的指针,指向我们要写入的文件流。函数返回值为非负整数表示成功,如果写入出错,返回EOF。
#include<stdio.h> #include<stdlib.h> int main() { FILE* file = fopen("./test.txt", "w"); if (file == NULL) { perror("fopen"); return 1; } char arr[30] = "qwertytuio"; int a = fputs(arr, file); printf("%d", a); fclose(file); file = NULL; return 0; }
文本字符串输入函数
str是一个指向字符数组的指针,用于存储读取到的字符串;n是要读取的最大字符数(包括空字符);stream是一个指向文件的指针,指向我们要读取的文件流。函数返回值是一个指向存储读取到的字符串的指针,如果到达文件末尾或者读取出错,返回NULL。
需要注意的是在文件中真正读取的字符是 n - 1,或者读取到\n或者读完文件内容就不会继续读下去
#include<stdio.h> #include<stdlib.h> int main() { FILE* file = fopen("./test.txt", "r"); if (file == NULL) { perror("fopen"); return 1; } char arr[20]; fgets(arr, 5, file); printf(arr); fclose(file); file = NULL; return 0; }
格式化输出函数
如果我们对比printf函数就会发现.fprintf多了stream这个参数,
stream是一个指向文件的指针,指向我们要写入的文件流;format是一个格式化字符串,用于指定写入数据的格式;…表示可变参数,用于提供要写入的具体数据。
成功后,将返回写入的字符总数。
如果发生写入错误,则设置错误指示器(ferror)并返回负数。
如果在写入宽字符时发生多字节字符编码错误,errno 将设置为 EILSEQ 并返回负数。
#include<stdio.h> #include<stdlib.h> int main() { FILE* file = fopen("./test.txt", "w+"); if (file == NULL) { perror("fopen"); return 1; } fprintf(file, "%d\n", 123456789); char arr[11]; fclose(file); file = NULL; //光标回复 FILE* file1 = fopen("./test.txt", "r"); if (file1 == NULL) { perror("fopen"); return 1; } fgets(arr, 10, file1); printf(arr); fclose(file1); file1 = NULL; return 0; }
格式化输入函数
如果和scanf函数对比就会发现fscanf只是多了stream参数
stream 是指向要读取的文件的指针,format 是格式字符串,用于指定要读取的数据类型和格式。
成功后,该函数返回成功填充的参数列表的项数。此计数可以与预期的项目数匹配,也可以由于匹配失败、读取错误或文件末尾的到达而减少(甚至为零)。
如果发生读取错误或在读取时到达文件末尾,则会设置正确的指示器(feof 或 ferror)。并且,如果在成功读取任何数据之前发生任一情况,则返回 EOF。
如果在解释宽字符时发生编码错误,该函数会将 errno 设置为 EILSEQ。
#include<stdio.h> #include<stdlib.h> int main() { FILE* file = fopen("./test.txt", "r"); if (file == NULL) { perror("fopen"); return 1; } int a; fscanf(file, "%d", &a); printf("%d", a); fclose(file); file = NULL; file = fopen("./test.txt", "w"); if (file == NULL) { perror("fopen"); return 1; } fprintf(file, "%d", 11111111); fclose(file); file = NULL; return 0; }
C语言进阶第十课 --------文件的操作-2