目录
思维导图:
1. 为什么使用文件
在学习通讯录的时候,我们往通讯录里添加信息,
但是,当我们关闭程序的时候,存放在通讯录的内容就销毁了。
而使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。
如果把通讯录数据放文件了,下一次打开也能继承上一次的数据。
2. 什么是文件
磁盘上的文件就是文件。
但是在程序设计中,我们一般谈的文件有两种:程序文件和数据文件。
2.1 程序文件
程序文件包括:
1.源程序文件(后缀为.c)
2.目标文件(windows环境后缀为.obj)
3.可执行程序(windows环境后缀为.exe)
2.2 数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,
比如程序运行需要从中读取数据的文件,或者输出内容的文件。
2.3 文件名
一个文件有一个唯一的文件标识,
文件名包含3部分:文件路径+文件名主干+文件后缀,
例: c:\code\test.txt
3. 文件的打开和关闭
3.1 文件指针
在C语言中,我们需要知道的是 FILE* 是文件类型的指针,
他的具体实现是C语言已经帮我们实现了的。
3.2 文件的打开和关闭
文件的打开使用的是 fopen 函数:
文件的关闭使用的是 fclose 函数:
直接上代码:
例:
int main()
{
//打开并读取这个文件名的文件
FILE* pf = fopen("test.txt", "r");
//判断打开文件是否成功
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
输出:
输出;fopen: No such file or directory
因为磁盘中并没有这个文件,所以报错。
对文件的操作还有很多:
文件使用方式 | 含义 | 如果指定文件不存在 |
“r”(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
“w”(只写) | 为了输出数据,打开一个文本文件 | 建立一个新的文件 |
“a”(追加) | 向文本文件尾添加数据 | 建立一个新的文件 |
“rb”(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
“wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
“ab”(追加) | 向一个二进制文件尾添加数据 | 出错 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“w+”(读写) | 为了读和写,建议一个新的文件 | 建立一个新的文件 |
“a+”(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
“rb+”(读写) | 为了读和写打开一个二进制文件 | 出错 |
“wb+”(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
“ab+”(读写) | 打开一个二进制文件,在文件尾进行读和写 | 建立一个新的文件 |
之后再用到的时候需要对其一定的了解。
4. 文件的顺序读写
接下来我们学习一些对文件读写的函数,
这样我们才能更好的操作文件。
功能 | 函数名 | 适用于 |
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输出流 |
文本行输入函数 | fgets | 所有输入流 |
文本行输出函数 | fputs | 所有输出流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprintf | 所有输出流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
我们通过一些例子来学习一下上述函数:
字符输出函数 fputc:
例1:
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
fputc('a', pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
我们先通过 fputc 函数写了个字符‘a’进文件test.txt中。
打开文件:
文件中真的写入了字符‘a’。
字符输入函数 fgetc:
我们还能通过 fgetc 函数读文件验证一下:
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c\n", ch);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
输出:
我们发现真的读出并打印出了字符‘a’。
接下来我们学习一下 fputs 函数和 fgets 函数。
例:
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
fputs("welcome to my blog\n", pf);
fputs("happy new year\n", pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
输出:
写完文件之后,自然是读出来看看:
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
char buf[20] = { 0 };
fgets(buf, 20, pf);//只读一行
printf("%s\n", buf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
我们发现 fgets 函数只读一行:
只要我们再用一次 fgets 函数就行:
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
char buf[20] = { 0 };
fgets(buf, 20, pf);//只读一行
printf("%s", buf);
fgets(buf, 20, pf);
printf("%s", buf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
输出:
接下来是 fprintf 函数,格式化写入文件:
例:
#include <stdio.h>
typedef struct S
{
char name[20];
int age;
float score;
}S;
int main()
{
S s = { "zhagnsan",120,2.22 };
S* ps = &s;
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//格式化写入文件
fprintf(pf, "%s %d %f\n", ps->name, ps->age, ps->score);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
输出:
文件成功写入了,
接下来是格式化输出 fscanf 函数:
例:
#include <stdio.h>
typedef struct S
{
char name[20];
int age;
float score;
}S;
int main()
{
S s = { "zhagnsan",120,2.22 };
S* ps = &s;
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//格式化的读文件
fscanf(pf, "%s %d %f", ps->name, &(ps->age), &(ps->score));
printf("%s %d %f\n", ps->name, ps->age, ps->score);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
输出:
最后是二进制写入文件 fwrite 函数:
#include <stdio.h>
typedef struct S
{
char name[20];
int age;
float score;
}S;
int main()
{
S s = { "zhagnsan",120,2.22 };
S* ps = &s;
FILE* pf = fopen("test.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
fwrite(&s, sizeof(S), 1, pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
输出:
因为写进去的是二进制的形式,所以就出现了一些你看不懂的东西。
接着,我们用二进制的形式读取文件 fread 函数:
#include <stdio.h>
typedef struct S
{
char name[20];
int age;
float score;
}S;
int main()
{
S s = { "zhagnsan",120,2.22 };
//S s = {0};
S* ps = &s;
FILE* pf = fopen("test.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
fread(&s, sizeof(S), 1, pf);
printf("%s %d %f", s.name, s.age, s.score);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
输出:
成功打印出来。
4.1 对比一组函数
scanf / fscanf / sscanf
printf / fprintf / sprintf
例:
#include <stdio.h>
typedef struct S
{
char name[20];
int age;
float score;
}S;
int main()
{
S s = { "zhagnsan",120,2.22 };
char buf[100] = { 0 };
sprintf(buf, "%s %d %f", s.name, s.age, s.score);
printf("%s\n", buf);
S tmp;
sscanf(buf, "%s %d %f", tmp.name, &(tmp.age), &(tmp.score));
printf("%s %d %f", tmp.name, tmp.age, tmp.score);
return 0;
}
输出:
输出:
zhagnsan 120 2.220000
zhagnsan 120 2.220000
5. 文件的随机读写
5.1 fseek
根据文件指针的位置和偏移量来定位文件指针。
5.2 ftell
返回文件指针相对于起始位置的偏移量。(计算文件指针所在位置)
5.3 rewind
让文件指针的位置回到文件的起始位置。
事先在文件中存储"abcdef"。
例:
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
}
else
{
int ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
fseek(pf, -2, SEEK_CUR);//文件指针偏移
ch = fgetc(pf);//查看偏移量
printf("%c\n", ch);
rewind(pf);//文件指针返回起始位置
printf("%d\n", ftell(pf));
fclose(pf);
pf = NULL;
}
return 0;
}
输出:
输出:
a
b
c
b
0
6. 文本文件和二进制文件
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
例:
#include <stdio.h>
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
fclose(pf);
pf = NULL;
return 0;
}
然后再VS中打开这个文件:
然后就能看到:
我们存进内存的数以小端形式存储,
这里展现出的是以十六进制打印出来的结果,其本质就是二进制数。
7. 文件读取结束的判定
7.1 feof
这个函数是用于判断文件读取结束的原因:
如果文件是因为读取失败而结束就返回0,
如果文件是遇到文件尾结束就返回非0数。
8. 文件缓冲区
如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),
然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。
缓冲区的大小根据C编译系统决定的。
例:
如果缓冲区没满,主动刷新缓冲区也会将数组存入磁盘。
而C语言中调用 fclose 函数就会刷新一下,
所以,写程序如果打开了文件,记得一定要关闭,否则肯数据就丢失了。
写在最后:
以上就是本篇文章的内容了,感谢你的阅读。
如果喜欢本文的话,欢迎点赞和评论,写下你的见解。
如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。
之后我还会输出更多高质量内容,欢迎收看。