目录
一.了解文件
1.文件主要功能
- 使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。
- 在以前各章所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显 示器上。
- 其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理 的就是磁盘上文件。
2.什么是文件
磁盘上的文件是文件。
但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。
2.1程序文件
包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境 后缀为.exe)。
2.2数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件, 或者输出内容的文件。
2.3 文件名
文件名包含3部分:文件路径+文件名主干+文件后缀
比如: c:\code\test.txt
二.文件的打开和关闭
以下所有讲的函数头文件都是:#include <stdio.h>
1.文件指针
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
我们可以创建一个FILE*的指针变量:
FILE* pf;//文件指针变量
每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息, 使用者不必关心细节。
例如,VS2013编译环境提供的 stdio.h 头文件中有以下的文件类型申明:
不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。
2.文件的打开和关闭
1.打开文件函数:
代码演示:
FILE *pf = fopen("test.txt", "w");
文件名:
文件名分为两种形式:
1.绝对路径
文件路径+文件名主干+文件后缀
2.相对路径
文件名主干+文件后缀(也可以不加后缀)
相对路劲是在当前程序文件的文件夹下,去查找
文件的打开方式:
文件使用方式 含义 如果指定文件不存在 “r”(只读) 为了输入数据,打开一个已经存在的文本文件 出错 “w”(只写) 为了输出数据,打开一个文本文件 建立一个新的文件 “a”(追加) 向文本文件尾添加数据 建立一个新的文件 “rb”(只读) 为了输入数据,打开一个二进制文件 出错 “wb”(只写) 为了输出数据,打开一个二进制文件 建立一个新的文件 “ab”(追加) 向一个二进制文件尾添加数据 出错 “r+”(读写) 为了读和写,打开一个文本文件 出错 “w+”(读写) 为了读和写,建议一个新的文件 建立一个新的文件 “a+”(读写) 打开一个文件,在文件尾进行读写 建立一个新的文件 “rb+”(读写) 为了读和写打开一个二进制文件 出错 “wb+”(读写) 为了读和写,新建一个新的二进制文件 建立一个新的文件 “ab+”(读写) 打开一个二进制文件,在文件尾进行读和写 建立一个新的文件
返回:
正确代码书写:
//打开文件 FILE *pf = fopen("test.txt", "w"); //文件是有可能打开失败的,文件名出错,打开方式问题等 if (NULL == pf) { perror("fopen"); return; }
只有打开文件就一定要搭配上判断
2.关闭文件函数
代码演示:
fclose(pf);
正确的代码规范:
int main()
{
//打开文件
FILE *pf = fopen("test.txt", "w");
//文件是有可能打开失败的,文件名出错,打开方式问题等
if (NULL == pf)
{
perror("fopen");
return;
}
//写文件
//……
//关闭文件
fclose(pf);
pf = NULL;//防止再次使用此指针
return 0;
}
三.顺序读写文件函数
- 这里的输入是指把文件的数据输入到程序内存中
- 输出指的是内存中的数据,输出到文件中
站在程序内存的角度,把文件想成平时打印的cmd黑框就好
功能 | 函数名 | 适用于 |
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输出流 |
文本行输入函数 | fgets | 所有输入流 |
文本行输出函数 | fputs | 所有输出流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprintf | 所有输出流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
fputc字符输入函数
函数原型:
补充:输入流是什么 后面讲现在你们看一下代码就懂
函数代码:
这是我当前文件下的一些文件,我讲利用打开方式自动创建一个文件
运行程序前的文件夹:
代码:
#include<stdio.h> int main() { //打开文件 FILE* pf = fopen("test.txt", "w"); if (NULL == pf) { perror("fopen"); return 1; } //写文件 int i = 0; for (i = 0; i < 26; i++) { fputc('a' + i, pf);//写入26个字母 } //关闭文件 fclose(pf); pf = NULL; return 0; }
运行程序前的文件夹:
你们可以去看看是不是跟我打开的文件夹名相同,而且已经在里面写入了26个字母:
返回:
函数正常执行返回所写数据,字符返回对应ASII码值
发生写入错误返回EOF
fgetc字符输入函数
函数原型
返回:
函数正常执行返回所写数据,字符返回对应ASII码值
发生写入错误返回EOF
代码:
我会读取刚刚fgetc输入到文件中的数据
注意:我打开方式的变化
#include<stdio.h> int main() { //打开文件 FILE* pf = fopen("test.txt", "r"); if (NULL == pf) { perror("fopen"); return 1; } //读文件 int ch; while ((ch = fgetc(pf)) != EOF) { printf("%c ", ch); } //关闭文件 fclose(pf); pf = NULL; return 0; }
fgetc读取了一个字符会自动向后移动一位
结果:
fputs文本行输出函数
函数原型:
返回:
代码:
一次输出一行
#include<stdio.h> int main() { //打开文件 FILE* pf = fopen("test.txt", "w"); if (NULL == pf) { perror("fopen"); return 1; } //写文件 fputs("hallo\n", pf); fputs("word", pf); //关闭文件 fclose(pf); pf = NULL; return 0; }
结果:
有此可见转意字符\n等也是可以操作的
补充:因为文件方式的原因会把之前的数据覆盖掉,成为新的数据如果想不覆盖要用追加的方式打开,我现在把打开方式改为追加执行
#include<stdio.h> int main() { //打开文件 FILE* pf = fopen("test.txt", "a");//注意我把打开方式改为了追加a if (NULL == pf) { perror("fopen"); return 1; } //写文件 fputs("hallo\n", pf); fputs("word", pf); //关闭文件 fclose(pf); pf = NULL; return 0; }
结果:
还有一个二进制的追加ab,其余打开方式请自行去查看上面的表格
fgets文本行输入函数
函数原型:
返回:
代码:
一次输入一行
#include<stdio.h> int main() { //打开文件 FILE* pf = fopen("test.txt", "r"); if (NULL == pf) { perror("fopen"); return 1; } //读文件 char arr[] = "#######################";//用来观察一下的字符串 fgets(arr, 20, pf); printf("%s", arr);//注意我这里没有加\n因为之前的输入里面是输入进去了\n的 fgets(arr, 20, pf); printf("%s", arr); //关闭文件 fclose(pf); pf = NULL; return 0; }
注意我这里没有加\n因为之前的输入里面是输入进去了\n的
结果:
fprintf格式化输出函数
函数原型:
可以发现只是多了一个参数其余并没有变化
返回:
代码:
格式化输出数据到文件中
#include<stdio.h> struct S { char name[20]; int age; float scores; }; int main() { //打开文件 struct S s = { "Laoli" ,20 ,95.5f }; FILE* pf = fopen("test.txt", "w"); if (NULL == pf) { perror("fopen"); return 1; } //写文件 fprintf(pf, "%s %d %f\n", s.name, s.age, s.scores); //关闭文件 fclose(pf); pf = NULL; return 0; }
这个字符在输出字符串时遇到空格也会正常输出
结果:
fscanf格式化输入函数
函数原型:
返回:
代码:
把刚刚放进文件的数据格式化拿出来
#include<stdio.h> struct S { char name[20]; int age; float scores; }; int main() { //打开文件 struct S s = { 0 }; FILE* pf = fopen("test.txt", "r"); if (NULL == pf) { perror("fopen"); return 1; } //读文件 fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.scores)); printf("%s %d %f", s.name, s.age, s.scores); //关闭文件 fclose(pf); pf = NULL; return 0; }
结果:
这个函数在遇到空格会停止输入所有项,例如在Laoli中间加上空格:Lao li
fwrite二进制输出函数
函数原型:
这个函数是以二进制的方式进行输出的
返回:
代码:
#include<stdio.h> struct S { char name[20]; int age; float scores; }; int main() { //打开文件 struct S s = { "Laoli" ,20 ,95.5f }; FILE* pf = fopen("test.txt", "wb");//注意我已经改变了文件的打卡方式 if (NULL == pf) { perror("fopen"); return 1; } //写文件 fwrite(&s, sizeof(struct S), 2, pf); //关闭文件 fclose(pf); pf = NULL; return
注意我已经改变了文件的打卡方式
结果:
大家可以看到除了字符串什么都看不懂呀,这个烫烫烫烫烫烫是什么鬼呀,其实是因为二进制的形式存储的,计算机可以看懂就可以了,也可以利用fread函数查看内容
fread二进制输入函数
函数原型:
返回:
代码:
读取刚刚存入文件的数据,以二进制形式读取
#include<stdio.h> struct S { char name[20]; int age; float scores; }; int main() { //打开文件 struct S s = { 0 }; FILE* pf = fopen("test.txt", "rb"); if (NULL == pf) { perror("fopen"); return 1; } //读文件 fread(&s, sizeof(struct S), 1, pf); printf("%s %d %f", s.name, s.age, s.scores); //关闭文件 fclose(pf); pf = NULL; return 0; }
结果:
四. 解析上述的流
以上函数都是在文件流中拿信息,是需要fopen函数来打开的文件流
还几个默认打开的流:
代码:
五.文件的随机读写
其实这些函数也并不是随机的,也是有规律的,但不再是从开头到结尾了
fseek
函数原型:
偏移量(字节为单位):
向右移动用正数,向左移动用负数
起始位置设置:
返回:
代码:
我手动的在文件中添加这样的数据:
从末尾移动三位取出数据:
#include<stdio.h> int main() { //打开文件 FILE* pf = fopen("test.txt", "rb"); if (NULL == pf) { perror("fopen"); return 1; } fseek(pf, -3, SEEK_END); int ch = fgetc(pf); printf("%c\n", ch); //关闭文件 fclose(pf); pf = NULL; return 0; }
ftell
功能:返回文件指针相对于起始位置的偏移量
函数原型:
返回:
成功返回当前位置
代码:
我们就用刚刚的代码加上函数试一下:
#include<stdio.h> int main() { //打开文件 FILE* pf = fopen("test.txt", "rb"); if (NULL == pf) { perror("fopen"); return 1; } fseek(pf, -3, SEEK_END); int ch = fgetc(pf); printf("%c\n", ch); int pos = ftell(pf); printf("%d", pos); //关闭文件 fclose(pf); pf = NULL; return 0; }
结果:
分析:
rewind
功能:让文件指针的位置回到文件的起始位置
函数原型:
代码:
#include<stdio.h> int main() { //打开文件 FILE* pf = fopen("test.txt", "rb"); if (NULL == pf) { perror("fopen"); return 1; } fseek(pf, -3, SEEK_END); int ch = fgetc(pf); printf("%c\n", ch); rewind(pf);//回到文件的起始位置 int pos = ftell(pf); printf("%d", pos); //关闭文件 fclose(pf); pf = NULL; return 0; }
结果:
这个函数非常简单不做过多介绍了
此片文章设计大量函数,记不住很正常,收藏一波需要的时候来查一查。