【通关文件操作(上)】--文件的意义和概念,二进制文件和文本文件,文件的打开和关闭,文件的顺序读写
目录
一.文件的意义和概念
1.1--为什么要使用文件?&& 什么是文件?
1.2--程序文件 && 数据文件
1.3--文件名
二.二进制文件和文本文件
三.文件的打开和关闭
3.1--流和标准流
3.1.1--流
3.1.2--标准流
3.2--文件指针
3.3--文件的打开和关闭
3.3.1--fopen函数
3.3.2--fclose函数
四.文件的顺序读写
4.1--fputc函数
4.2--fgetc函数
4.3--feof和ferror函数
4.4--fputs函数
4.5--fgets函数
4.6--fprintf函数
4.7--fscanf函数
🔥个人主页:@草莓熊Lotso的个人主页
🎬作者简介:C++研发方向学习者
📖个人专栏:《C语言》
⭐️人生格言:生活是默默的坚持,毅力是永久的享受。
一.文件的意义和概念
1.1--为什么要使用文件?&& 什么是文件?
--如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件。
--磁盘(硬盘)上的文件是文件。但是在程序设计中,我们⼀般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。
1.2--程序文件 && 数据文件
- 程序文件包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)
数据文件:
- 文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

1.3--文件名
--一个文件要有唯一个的文件标识,以便我们识别和使用。
文件名包含3个部分:文件路径+文件名主干+文件后缀
例如:D:\code\test.txt
二.二进制文件和文本文件
- 数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的文件中,就是二进制文件。
- 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
- 字符⼀律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
- 如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符⼀个字节),而二进制形式输出,则在磁盘上只占4个字节。

代码演示:
#include <stdio.h>int main()
{int a = 10000;FILE* pf = fopen("data.txt", "wb");if (pf == NULL){perror("fopen");return 1;}fwrite(&a, 4, 1, pf);//二进制的形式写到文件中//关闭文件fclose(pf);pf = NULL;return 0;
}
我们默认打开的会是文本文件,观察不了二进制形式,所以我们需要在VS上打开二进制文件 ,具体操作如下:
我们把data.txt的打开方式设置为二进制编辑器,就可以观察二进制文件了。
三.文件的打开和关闭
3.1--流和标准流
3.1.1--流

3.1.2--标准流
- stdin - 标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据。
- stdout - 标准输出流,大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出
- 流中。
- stderr - 标准错误流,大多数环境中输出到显示器界面。
这就是是默认打开的三个流,所以我们使用scanf、printf等函数就可以直接进行输入输出操作。stdin、stdout、stderr 三个流的类型是: FILE * ,通常称为文件指针。 C语言中,就是通过 FILE* 的文件指针来维护流的各种操作的。
3.2--文件指针
--缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
struct _ iobuf {char *_ptr;int _cnt;char *_base;int _flag;int _file;int _charbuf;int _bufsiz;char *_tmpfname;};typedef struct _ iobuf FILE ;
FILE* pf; //文 件指针变量
3.3--文件的打开和关闭
--文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回⼀个FILE*的指针变量指向该⽂件,也相当于建立了指针和文件的关系。
3.3.1--fopen函数
//打开文件1. FILE * fopen ( const char * filename , const char * mode );
功能:
fopen函数是用来打开参数filename所指定的文件的,同时将其和一个流进行关联,后续对流的操作是通关fopen函数返回的指针来维护的。具体对流的操作是通过参数mode指定的
参数:
- filename:表示被打开的文件的名字,这个名字可以是绝对路径,也可以是相对路径。
- mode:表示对打开文件的操作方式,后面会有具体解析
返回值:
- 如果文件成功打开,该函数返回一个指向FILE对象的指针,该指针可以用于后续操作中标识对应的流
- 如果打开失败,则返回NULL指针,所以我们要对fopen的返回值进行判断,来验证文件是否成功打开
代码演示:这里演示5种不同情况
//1.data.txt在当前工程的目录下
int main()
{FILE* pf = fopen("data.txt", "r");//这里的路径是相对路径,表示在当前的工程目录下的data.txtif (pf == NULL){perror("fopen");return 1;}//读取文件//关闭文件return 0;
}//2. . --表示当前路径, .. --表示上一级路径
//所以如果data.txt在上一级路径下的时候应该这样写
int main()
{FILE* pf = fopen("./../data.txt", "r");//这里的路径是相对路径,表示在当前的工程目录下的data.txtif (pf == NULL){perror("fopen");return 1;}//读取文件//关闭文件return 0;
}//3.如果data.txt在上二级路径下的时候应该这样写
int main()
{FILE* pf = fopen("./../../data.txt", "r");//这里的路径是相对路径,表示在当前的工程目录下的data.txtif (pf == NULL){perror("fopen");return 1;}//读取文件//关闭文件return 0;
}//4.如果data.txt在上二级路径下的文件夹text中的text2文件夹里的时候应该这样写
int main()
{FILE* pf = fopen("./../../text/text2/data.txt", "r");//这里的路径是相对路径,表示在当前的工程目录下的data.txtif (pf == NULL){perror("fopen");return 1;}//读取文件//关闭文件return 0;
}//5.如果data.txt在桌面上,就得用绝对路径了
int main()
{FILE* pf = fopen("C:/Users/LOTSO/Desktop/data.txt", "r");//这里的路径是绝对路径if (pf == NULL){perror("fopen");return 1;}//读取文件//关闭文件return 0;
}
mode--文件操作方式:
文件使用方式 | 含义 | 如果指定文件不存在 |
"r"(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
"w"(只写) | 为了输出数据,打开⼀个文本文件 | 建立⼀个新的文件 |
“a”(追加) | 向文本文件尾添加数据 | 建立⼀个新的文件 |
“rb”(只读) | 为了输入数据,打开⼀个二进制文件 | 出错 |
"wb"(只写) | 为了输出数据,打开⼀个二进制文件 | 建立⼀个新的文件 |
“ab”(追加) | 向⼀个二进制文件尾添加数据 | 建立⼀个新的文件 |
“r+”(读写) | 为了读和写,打开⼀个文本文件 | 出错 |
“w+”(读写) | 为了读和写 建立⼀个新的文件 | 建立一个新的文件 |
“a+”(读写) | 打开⼀个文件,在文件尾进行读写 | 建立一个新的文件 |
“rb+”(读写) | 为了读和写打开⼀个二进制文件 | 出错 |
“wb+”(读 写) | 为了读和写,新建⼀个新的二进制文件 | 建立一个新的文件 |
“ab+”(读 写) | 打开⼀个二进制文件,在文件尾进行读和写 | 建立一个新的文件 |
----------------- | ------------------------------------------------------ | ------------------------ |
3.3.2--fclose函数
1. int fclose ( FILE * stream );
- stream :指向要关闭的流的 FILE 对象的指针
代码演示:
#include<stdio.h>int main()
{FILE* pf = fopen("data.txt", "r");//这里的路径是相对路径,表示在当前的工程目录下的data.txtif (pf == NULL){perror("fopen");return 1;}//读取文件//关闭文件fclose(pf);pf = NULL;return 0;
}
四.文件的顺序读写
在进行文件写的时候,我们会涉及下面这些函数:
函数名 | 功能 | 适用于 |
fgetc | 从输入流中读取一个字符 | 所有输入流 |
fputc | 向输出流中写入一个字符 | 所有输出流 |
fgets | 从输入流中读取一个字符串 | 所有输入流 |
fputs | 向输出流中写入一个字符串 | 所有输出流 |
fscanf | 从输入流中读取带有格式的数据 | 所有输入流 |
fprintf | 向输出流中写入带有格式的数据 | 所以输出流 |
fread | 从输入流中读取一块数据 | 文件输入流 |
fwrite | 从输出流中写入一块数据 | 文件输出流 |
4.1--fputc函数
1. int fputc ( int character, FILE * stream );
- character :被写入的字符
- stream :是⼀个FILE*类型的指针,指向了输出流(通常是文件流或stdout)。
- 成功时返回写入的字符(以 int 形式)。
- 失败时返回 EOF (通常是 -1),错误指示器会被设置,可通过 ferror() 检查具体错误。
代码演示:
//fputc函数演示
#include<stdio.h>
int main()
{FILE* ps = fopen("data.txt", "w");//w--写,用这个才可以写文件if (ps == NULL){perror("fopen");return 1;}//写文件fputc('a', ps);fputc('b', ps);fputc('c', ps);//关闭文件fclose(ps);ps = NULL;return 0;
}//循环写入多个字符
int main()
{FILE* ps = fopen("data.txt", "w");//w--写,用这个才可以写文件if (ps == NULL){perror("fopen");return 1;}//写文件for (int i = 'a';i <= 'z';i++){fputc(i, ps);}//关闭文件fclose(ps);ps = NULL;return 0;
}
//直接打印在屏幕上,标准输出流
#include<stdio.h>
int main()
{fputc('a', stdout);fputc('b', stdout);fputc('c', stdout);return 0;
}
4.2--fgetc函数
1. int fgetc ( FILE * stream );
- stream : FILE*类型的文件指针,可以是 stdin ,也可以是其他输入流的指针。如果是 stdin 就从标准输入流读取数据。如果是文件流指针,就从文件读取数据。
- 成功时返回读取的字符(以 int 形式)。
- 若调用时流已处于文件末尾,函数返回 EOF 并设置流的文件结束指示器( feof )。
- 若发生读取错误,函数返回 EOF 并设置流的错误指示器( ferror )。
代码演示:
//fgetc函数演示,假设文件里是26个字母
#include<stdio.h>
int main()
{FILE* ps = fopen("data.txt", "r");if (ps == NULL){perror("fopen");return 1;}//读文件for (int i = 0;i < 10;i++){int c=fgetc(ps);fputc(c, stdout);}//关闭文件fclose(ps);ps = NULL;return 0;
}
从键盘读取数据
#include<stdio.h>
int main()
{//读数据int c=fgetc(stdin);fputc(c, stdout);return 0;
}
4.3--feof和ferror函数
int feof ( FILE * stream );// 检测 stream 指针指向的流是否遇到文件末尾int ferror ( FILE * stream );// 检测 stream 指针指向的流是否发生读 / 写错误
- 如果在读取文件的过程中,遇到了文件末尾,文件读取就会结束。这时读取函数会在对应的流上设置⼀个文件结束的指示符,这个为文件结束指示符可以通过 feof 函数检测到。如果 feof 函数检测到文件结束指示符已经被设置,则返回非0的值,如果没有设置则返回0。
- 如果在读/写文件的过程中,发生了读/写错误,文件读取就会结束。这时读/写函数会在对应的流上设置⼀个错误指示符,这个错误指示符可以通过 ferror 函数检测到。如果 ferror 函数检测到错误指示符已经被设置,则返回非0的值,如果没有设置则返回0。
测试feof:
//测试feof,假设文件里是abcdef
#include<stdio.h>
int main()
{FILE* ps = fopen("data.txt", "r");if (ps == NULL){perror("fopen");return 1;}//读文件int i = 0;for (i = 0;i < 10;i++){int c = fgetc(ps);if (c == EOF){if (feof(ps)){printf("遇到文件结尾了\n");//读到f就结尾了}else if (ferror(ps)){printf("读取发生错误\n");}}else{fputc(c, stdout);}}fclose(ps);ps = NULL;return 0;
}
测试ferror:
//测试ferror,
// 以写的形式打开文件后,后面再读文件,就会发生错误
#include<stdio.h>
int main()
{FILE* ps = fopen("data.txt", "w");if (ps == NULL){perror("fopen");return 1;}//读文件int i = 0;for (i = 0;i < 6;i++){int c = fgetc(ps);if (c == EOF){if (feof(ps)){printf("遇到文件结尾了\n");}else if (ferror(ps)){printf("读取文件错误\n");}}else{fputc(c, stdout);}}fclose(ps);ps = NULL;return 0;
}
4.4--fputs函数
1. int fputs ( const char * str, FILE * stream );
- str : str是指针,指向了要写入的字符串(必须以 \0 结尾)
- stream :是⼀个 FILE* 的指针,指向了要写入字符串的流。
- 成功时返回非负整数。
- 失败时返回 EOF (-1),同时会设置流的错误指示器,可以使用 ferror() 检查错误原因。
代码演示:
//fputs函数演示
#include<stdio.h>
int main()
{FILE* ps = fopen("data.txt", "w");if (ps == NULL){perror("fopen");return 1;}//写文件fputs("abc\n", ps);fputs("def", ps);//关闭文件fclose(ps);ps = NULL;return 0;
}
和fputc函数一样,我们也可以直接打印在屏幕上,把ps改成stdout就可以,这里就不再演示了。
4.5--fgets函数
1. char * fgets ( char * str, int num, FILE * stream );
- str :是指向字符数组的指针,str指向的空间用于存储读取到的字符串。
- num :最大读取字符数(包含结尾的 \0 ,实际最多读取 num-1 个字符)。
- stream :输入流的文件指针(如文件流或 stdin )。
- 成功时返回 str 指针。
- 若在尝试读取字符时遇到⽂件末尾,则设置文件结束指示器,并返回 NULL ,需通过 feof()检测。
- 若发生读取错误,则设置流错误指示器,并返回 NULL,通过 ferror() 检测。
代码演示:
// fgets函数演示,假设文件里还是上面fputs函数写的 abc\n def
#include<stdio.h>
int main()
{FILE* fp = fopen("data.txt", "r");if (fp == NULL){perror("fopen");return 1;}char arr1[] = "*************";/*char* f = fgets(arr1, sizeof(arr1), fp);fputs(f, stdout);*///全部打印出来abcchar*p=fgets(arr1,2, fp);//实际读取的是num-1个fputs(p,stdout);//所以是a//不再使用文件时,需要关闭文件fclose(fp);fp = NULL; //将指针置为NULL,避免成为野指针。return 0;
}
我们再来看一个例子并且通过调试以及打印在屏幕上来观察:
//把abc\n def全读出来,调试着看并打印在屏幕上
#include <stdio.h>
int main()
{FILE* fp = fopen("data.txt", "r");if (fp == NULL){perror("fopen\n");return 1;}char arr1[] = "*************";char*p=fgets(arr1, sizeof(arr1), fp);fputs(p, stdout);char arr2[] = "*************";char* f = fgets(arr2, sizeof(arr1), fp);fputs(f, stdout);//不再使用文件时,需要关闭文件fclose(fp);fp = NULL; //将指针置为NULL,避免成为野指针。return 0;
}
和fgetc函数一样,我们也可以直接从键盘上读取,把fp改成stdin就可以,这里也不再演示了。
4.6--fprintf函数
1. int fprintf ( FILE * stream, const char * format, ... );
2. //我们类比printf看看
3. int printf ( const char * format, ... );
- stream :指向 FILE 对象的指针,表⽰要写⼊的⽂件流(如 stdout 、文件指针等)。
- format :格式化字符串,包含要写⼊的⽂本和格式说明符(如 %d 、 %s 等)。
- ... :可变参数列表,提供与格式字符串中说明符对应的数据。
- 成功时,返回写入的字符总数(非负值)。
- 失败时,先设置对应流的错误指示器,再返回负值,可以通过 ferror() 来检测。
代码演示:
// fprintf函数演示
#include <stdio.h>
struct stu
{char name[30];int age;float score;
};
int main()
{struct stu s = { "zhangsan",18,95.5f};FILE* fp = fopen("data.txt", "w");if (fp == NULL){perror("fopen");return 1;}//写文件fprintf(fp, "%s %d %f", s.name, s.age, s.score);//关闭文件fclose(fp);fp = NULL;return 0;
}
当然这也可以直接打印在屏幕上,还是把fp换成stdout,这样效果等同于printf,这里就不演示了。
4.7--fscanf函数
1. int fscanf ( FILE * stream, const char * format, ... );
2. //类比scanf看看
3. int scanf ( const char * format, ... );
- stream :指向 FILE 对象的指针,表⽰要读取的⽂件流(如 stdin 、文件指针等)。
- format :格式化字符串,定义如何解析输⼊数据(如 %d 、 %f 、 %s 等)。
- ... :可变参数列表,提供存储数据的变量地址(需与格式字符串中的说明符匹配)。
- 格式和数据匹配失败;
- 读取发生错误;
- 到达文件末尾(EOF)。
- 发生读取错误,会在对应流上设置错误指示符,则返回 EOF 。
- 到达文件末尾,会在对应流上设置文件结束指示符,则返回 EOF 。
代码演示:
//fsanf函数演示
struct stu
{char name[30];int age;float score;
};
int main()
{struct stu s = { 0 };FILE* fp = fopen("data.txt", "r");if (fp == NULL){perror("fopen");return 1;}//写文件fscanf(fp, "%s %d %f", s.name, &(s.age), &(s.score));fprintf(stdout, "%s %d %f", s.name, s.age, s.score);//打印数据在stdout//关闭文件fclose(fp);fp = NULL;return 0;
}
当然这也可以直接从键盘上读取,还是把fp换成stdin,这样效果等同于scanf,这里就不演示了。
--由于篇幅原因,后面还有几个函数,我们在下篇文章再一起来继续学习。
往期回顾:
【C语言动态内存管理】--动态内存分配的意义,malloc和free,calloc和realloc,常见的动态内存的错误,动态内存经典笔试题分析,柔性数组,总结C/C++中程序内存区域划分
【自定义类型-结构体】--结构体类型,结构体变量的创建和初始化,结构体内存对齐,结构体传参,结构体实现位段
【自定义类型-联合和枚举】--联合体类型,联合体大小的计算,枚举类型,枚举类型的使用
结语:本篇文章就到此结束了,继前面一篇文章后,在此篇文章中给大家分享了文件操作中的文件的意义和概念,二进制文件和文本文件,文件的打开和关闭,文件的顺序读写(部分)等知识点,下篇文章会接着从文件的顺序读写后面的几个函数开始。分析文件操作中的剩余知识点,如果文章对你有帮助的话,欢迎评论,点赞,收藏加关注,感谢大家的支持。