• 花了整整一天,总结了C语言所有常用的文件操作


    本文介绍了基本常用的C语言文件定义,文件的基本操作(文件指针定义和文件的打开和关闭),文件的顺序读写(各种常用的文件读写函数),文件的随机读写(对指针偏移量操作的函数),文本文件和二进制文件.读取结束判定的函数(feof和ferror)文件缓冲区的概念
    学会了用C语言对文件操作,可以保留程序运行过程中的产生的有价值的数据,使得下次程序运行仍能使用

    一.为什么使用文件

    在之前博客中写了C语言实现学生信息管理系统静态和动态两个版本->学生信息管理系统程序
    在程序运行过程中,可以给管理系统中对学生信息进行增删查改等各种功能.
    但是,在程序运行过程中,这些操作的数据都是存放在内存中的,
    而内存中保存的这些数据,在程序结束后都会被释放,
    下一次再运行这个程序时,之前所做的各种操作都不会在保留,
    数据又得重新录入,这样的学生信息管理系统使用起来就很麻烦
    而要使在程序运行过程中,将操作的内容保存下来,
    使下一次运行程序时仍能留下学生信息,只有我们选择删除数据时数据才会消失,这就涉及到数据持久化的问题.
    一般数据持久化的方法有把数据存放在磁盘文件、存放到数据库等方式。
    使用文件操作我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化

    二.什么是文件

    双击进入磁盘,可以看到很多文件,每个文件里面都有属于自己的内容
    这些文件都是保存着数据信息
    但是在程序设计中,从文件功能角度来看文件一般指的是两种:程序文件和数据文件

    1.程序文件的定义

    包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。

    2.数据文件的定义

    文件不一定指的是程序,还是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
    我们处理的输入输出的数据都是以终端为对象,即从终端的键盘上输入数据,最后再将运行结果输出到屏幕终端
    这些操作实际上都是操作文件,屏幕可以看作是一个文件,键盘也是一个文件
    我们打开一个c程序时,实际上会默认打开stdout标准输出流(屏幕文件)
    stdin标准输入流(键盘文件) stderr标准错误输出流(屏幕文件)在后文会详细讲到

    3.认识文件名

    每一个文件都有自己的文件标识-即文件名,以便用户识别和引用
    文件名包含3部分:文件路径+文件名主干+文件后缀
    文件路径指的这个文件在你电脑的具体位置,
    文件名主干一般就是具体描述这个文件的名字,
    文件名主干取名规则: 不能出现> < " * / \ | : ? 这些字符,且最多有255个字符
    文件后缀 表示的是这个文件的属性类型,如.txt表示这个文件是文本文件

    三.文件的基本操作

    基本操作包括 文件指针FILE*的概念 和在C语言中文件的打开和关闭操作fopen 和fclose函数
    这些都是在stdio.h头文件中存在的类型和函数

    1.文件指针是什么

    缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
    每个被使用的文件都在内存中开辟了一个相应的文件信息区,
    用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。
    这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE

    不同的C编译器的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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,
    使用者不必关心细节,一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便

    FILE *pf;//定义一个 文件指针类型的变量pf
    
    • 1

    定义pf是一个指向FILE类型数据的指针变量。
    可以使pf指向某个文件的文件信息区(是一个结构体变量)。
    通过该文件信息区中的信息就能够访问该文件。
    也就是说,通过文件指针变量能够找到与它关联的文件。

    在这里插入图片描述

    每一个文件类型的指针,指向对应的一个文件信息区,每个文件信息区里存放着对应文件的数据,而通过这个指针就可以访问这个文件信息区,给文件进行添加删除数据操作…

    2.文件的打开和关闭

    在我们操作文件前,应先打开对应的文件,在使用结束后需要关闭对应的文件.
    ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件.

    //打开文件
    FILE * fopen ( const char * filename, const char * mode );
    //关闭文件
    int fclose ( FILE * stream );
    
    • 1
    • 2
    • 3
    • 4

    而用fopen打开文件后会返回指向该文件信息区的FILE类型指针
    此时就可以定义FILE
    类型的变量接受对应的指针…

    3.fopen–打开文件操作

    调用fopen函数需要传两个参数,
    第一个是一个字符串,字符串内容表示要打开的文件名,
    这个文件名可以用绝对地址例如:c:\code\test.txt 表示的该文件绝对路径,直接精确定位文件位置然后打开文件
    也可以是相对路径,例如:test.txt 则此时只会在源程序所在的目录下打开名为text.txt文件
    , 第二个也是字符串类型数据,表示以什么方式打开文件
    下面是对应的各种基本的文件使用方式 以及含义 和 未存在当前文件时的处理方法

    文件使用方式含义如果指定文件不存在
    “r”(只读)为了输入数据,打开一个已经存在的文本文件出错
    “w”(只写)为了输出数据,打开一个文本文件建立一个新文件
    “a”(追加)向文本文件尾添加数据建立一个新文件
    “rb”(只读)为了输入数据,打开一个二进制文件出错
    “wb”(只写)为了输出数据,打开一个二进制文件建立一个新文件
    “ab”(追加)向一个二进制文件尾添加数据出错
    “r+”(读写)为了读和写,打开一个文本文件出错
    “w+”(读写)为了读和写,建议一个新的文件建立一个新文件
    “a+”(读写)打开一个文件,在文件尾进行读写建立一个新文件
    “rb+”(读写)为了读和写打开一个二进制文件出错
    “wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新文件
    “ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新文件

    根据上面打开方式 拿"r" 只读"w"只写 "a"追加 三种打开方式做介绍

    ①."r"以只读方式打开指定文件

    "r"表示以只读方式打开指定的文件,这种方式是用来从文件里读取数据的
    此时会根据你写的第一个参数文件名字符串根据是绝对地址还是相对地址去查找文件.
    此时有两种情况:
    1.如果不存在对应的文件名,则会返回NULL,并且会有一个报错信息
    2.如果存在对应的文件名,则会将对应这个文件打开一个文件信息区,文件信息区里包含了这个文件的所有信息,并且返回指向这个文件信息区的一个文件类型指针

    #define _CRT_SECURE_NO_WARNINGS
    #include
    int main()
    {
    	FILE* pf = fopen("test.txt", "r");
    	if (pf == NULL)
    	{
    		printf("当前目录下不存在此文件\n");
    		perror("fopen");
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    可以看到这个源程序的目录下此时为存在test.txt这个文件,如果此时打开就会返回NULL指针 .此时再用perror这个打印错误信息的函数输出错误信息↓

    在这里插入图片描述

    此时我们可以先在源程序所在目录下手动创建一个test.txt文件看看效果
    在这里插入图片描述

    此时运行程序 没有显示错误信息表示当前pf指针变量里已不再是空指针 表示已经打开了指定的文件信息区…↓

    在这里插入图片描述

    ②"w"以只写方式打开指定文件

    "w"表示以只写方式打开指定文件,因为这种方式是用来写数据到文件的
    此时有两种情况
    如果存在对应的文件名,会将此文件删除,然后创建一个同名同类型的文件
    如果不存在对应的文件名,会自动创建一个对应名称类型的文件

    测试一下此时目录下没有对应文件名的文件会是什么情况
    在这里插入图片描述

    运行程序后发现,程序不会显示不存在此文件说明pf不是NULL,并且打开所在目录发现已经新建了一个test.txt文件↓↓
    在这里插入图片描述

    当向已有的test.txt文件里输入一些信息然后再运行程序看看什么情况
    在这里插入图片描述

    运行程序再点开test.txt文件后发现原来的数据已经消失,表示已经将原来的文件删除 新建了一个同样文件名类型的空文件
    在这里插入图片描述

    ③ "a"以追加形式打开指定文件

    "a"表示以追加形式打开指定文件,追加表示的是如果文件里有数据则可以在原有数据后面添加新数据 此时有两种情况:
    1.不存在指定文件时,会新建一个文件,打开其文件信息区返回文件指针
    2.存在指定文件时,直接打开对应文件的文件信息区返回文件指针

    测试一下如果不存在指定文件名时的情况
    在这里插入图片描述

    发现此时已经新建了一个名为text.txt的文件
    在这里插入图片描述

    测试当往已有的text.txt文件里输入数据后再运行程序的情况
    在这里插入图片描述

    发现运行程序后,文件里的文本信息不会丢失,表示此时不会将这个文件删除直接使用这个文件↓↓↓
    在这里插入图片描述

    以上测试了"r"只读 "w"只写 "a"追加 形式打开文件的方式,具体使用什么方式打开文件要看使用场景,上面打开方式还有很多种文件方式都可以使用

    4.fclose–关闭文件操作

    此操作类似于动态内存管理里的free函数
    当我们操作了一个文件后,需要用fclose文件关闭函数将文件信息区关闭,
    否则程序在运行过程中此文件信息区一直被打开而不需要被使用会降低系统性能

    #define _CRT_SECURE_NO_WARNINGS
    #include
    int main()
    {
    	FILE* pf = fopen("test.txt", "a");
    	if (pf == NULL)
    	{
    		printf("当前目录下不存在此文件\n");
    		perror("fopen");
    	}
    	fclose(pf);  //将pf文件指针指向的文件信息区关闭
    	pf = NULL;  //将pf指针变量 里置为NULL
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    四.文件的读写操作

    读写操作包括了文件的顺序读写 和随机读写 以及各种常用的函数讲解

    1.文件的顺序读写

    文件顺序读写表示从文件信息区起始位置开始往文件信息区里读出或者写入数据
    文件信息区起始地址就相当于是文件指针偏移量为0时所指向的位置
    下面是各种常用的向文件信息区里输入或者输出数据的库函数功能 函数名和适用的流

    功能函数名适用于
    字符输入函数fgetc所有输入流
    字符输出函数fputc所有输出流
    文本行输入函数fgets所有输入流
    文本行输出函数fputs所有输出流
    格式化输入函数fscanf所有输入流
    格式化输出函数fprintf所有输出流
    二进制输入fread文件
    二进制输出fwrite文件

    ①.输入流和输出流的概念

    输入输出流可以看做往从文件里读取数据的过程和将数据输出到文件的过程
    流看作一个过程,也可以相当于一个通道

    适用于所有输入流:表示任何文件或其他可以是输入数据的程序文件或磁盘文件都可以往程序里输入数据,拿文件和键盘来说 ,键盘相当于是标准输入流,所有输入流表示可以从文件里读取数据,也可以从键盘上读取数据
    键盘实际也相当于是一个文件,文件名为stdin,在c程序使用时默认已经打开了此文件

    适用于所有输出流:表示可以将数据输出到任何文件里或者可以保存数据的程序里, 拿普通文件和屏幕来说,屏幕也被称为标准输出流. 所有输出流表示可以将数据输出到文件,也可以输出到屏幕上
    屏幕实际上也可以看成一个文件,文件名为stdout,在c程序打开时默认打开了屏幕文件
    而在文章前部分提到了了stdin 和stdout和stderr ,stderr表示的是将错误信息输出到屏幕上 ,stdin stdou stderr都可以看做是程序一开始就已经默认打开的文件,都可以直接使用

    适用于文件:一般表示只能将数据输出到普通文件上,常用于将数据以二进制形式输出到文件中,因为二进制形式是计算机读取的语言,所以不适用于所有输入输出流,一般保存在文件里,并且对于人来说可读性差.

    ②.fgetc和fputc字符输入输出函数

    在这里插入图片描述

    fgetc字符输入函数, 返回类型为int 形参类型为FILE*文件指针
    表示从文件信息区里文件指针当前偏移量处读取一个字符将这个字符的ASCLL码返回,并将文件指针偏移量往后移动一位
    如果未读取到字符则会返回EOF(一般值为-1)

    在这里插入图片描述

    fputc字符输出函数, 返回类型int 函数参数 int 和FILE*
    表示将一个整数以ASCLL码字符形式输出到文件指针指向的文件信息区的当前偏移量处,返回值是输出的字符 如果输出发生了错误则会返回EOF

    #define _CRT_SECURE_NO_WARNINGS
    #include
    int main()
    {
    	FILE* pf = fopen("test.txt", "w");//以只写形式打开
    	if (pf == NULL)
    	{
    		printf("打开失败\n");
    		perror("fopen");
    	}
    
    	char c ;
    	int n=fputc('A', pf);  // 将字符A输出到pf指向的文件信息区
    
    	if (n == EOF)
    	{
    		printf("输出字符失败\n");
    	}
    
    	fclose(pf);  //将pf文件指针指向的文件信息区关闭
    	pf = NULL;
    	 pf = fopen("test.txt", "r");   //以只读形式打开
    	if (pf == NULL)
    	{
    		printf("打开失败\n");
    		perror("fopen");
    	}
    	n=fgetc(pf);
    	if (n == EOF)
    	{
    		printf("读取字符失败\n");
    	}
    	else
    	{
    		printf("%c", n);
    	}
    
    	fclose(pf);  //将pf文件指针指向的文件信息区关闭
    	pf = NULL;  //将pf指针变量 里置为NULL
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    在这里插入图片描述

    上面是fputc和fgetc的测试代码,先以只写形式打开test.txt文件
    往文件信息区里输入字符A,然后再关闭此文件信息区,再以只读形式打开test.txt文件 从文件信息区里读取第一个字符,则读取了前面输入的字符A

    为什么要关闭文件再打开而不直接读取?
    1.因为设置了是只写形式打开文件只能写不能读
    2.fputc输出一个字符到文件信息区里此时文件指针偏移量会往后移动一个位置表示此时指向文件里面A字符后面的一个位置,此时直接读取是读取不到字符A的需要使用fewind函数重置文件偏移量或者重新打开文件刷新偏移量

    ③.fgets和fputs文本行输入输出函数

    在这里插入图片描述

    fgets第一个参数为数组名用来接受读取的字符的数组
    第二个为要读取的字符串字符个数,
    第三个为文件指针 表示从文件里读取num-1个字符
    num是第二个参数,表示读取num-1个字符然后最后自动加上\0字符得到num个字符赋给第一个参数
    在读取过程中如果遇到\n会读取掉\n然后直接结束 读最后将读取到的字符串最后加上\0放在指定数组中(数组容量要大)
    读取成功会返回存放读取的字符串的字符指针,读取失败返回NULL

    在这里插入图片描述

    fputs文本行输出函数, 将字符串输出到文件指针指向的文件信息区,文件指针偏移量再往后移动输出的字符个数的大小
    输出成功返回非负值,失败返回EOF

    #define _CRT_SECURE_NO_WARNINGS
    #include
    int main()
    {
    	FILE* pf = fopen("text.txt", "w");
    	if (pf == NULL)
    	{
    		perror("fopen");
    	}
    	else
    	{
    		int n=fputs("123\n456", pf); //将123456输出到pf指向的文件信息区
    		if (n == EOF) //如果返回值为EOF则表示输出失败
    		{
    			printf("输出失败\n");
    		}
    		else  //否则输出成功
    		{
    			printf("输出成功\n");
    		}
    		fclose(pf);  //使用完后将文件信息区关闭
    		pf = NULL;
    	}
    	pf = fopen("text.txt", "r");
    	if (pf == NULL)
    	{
    		perror("fopen");
    	}
    	else
    	{
    		char a[7];
    		
    		char* tmp = fgets(a, 6, pf); //从pf指向的文件信息区读取 3个字符后加上\0共4个字符如果中途遇到\n则会直接读取\n后结束读取将读取的字符串在最后加上\n然后 存放在a数组中
    		if (tmp == NULL)
    		{
    			printf("读取信息失败\n");
    		}
    		else
    		{
    			printf("已读取信息:%s", a);
    			
    		}
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    在这里插入图片描述

    ④fscanf和fprintf格式化输入输出函数

    这两个函数和scanf printf差不多 多的就是一个文件指针

    在这里插入图片描述

    fscanf 比scanf最左边多了个参数,表示文件指针,其它的参数和scanf一样,
    表示从文件指针指向的文件信息区中读取对应格式的数据输入到对应的变量里
    最后返回的值是赋值的变量个数,未分配则返回EOF

    在这里插入图片描述

    fprintf()函数根据指定的format(格式)(格式)发送信息(参数)到由stream(流)指定的文件. fprintf()只能和printf()一样工作. fprintf()的返回值是输出的字符数,发生错误时返回一个负值.
    表示将对应变量里的值以对应格式输出到文件指针指向的信息区内,最后返回输出的字符个数,发生错误则返回复制

    #define _CRT_SECURE_NO_WARNINGS
    #include
    int main()
    {
    	FILE* pf = fopen("text.txt", "w");
    	if (pf == NULL)
    	{
    		perror("fopen");
    	}
    	else
    	{
    		int n=fprintf(pf, "%s", "Hello World");
    		if (n < 0)
    		{
    			printf("输出失败\n");
    		}
    		else
    		{
    			printf("输出成功\n");
    		}
    		fclose(pf);
    		pf = NULL;
    
    	}
    	 pf = fopen("text.txt", "r");
    	if (pf == NULL)
    	{
    		perror("fopen");
    	}
    	else
    	{
    		char arr[20];
    		fscanf(pf, "%s", arr);     //和scanf读取效果一样 遇到间隔符后结束读取 偏移量移到读取结束的位置
    		printf("读取的内容为:%s\n", arr); // 读取到Hello 遇到空格结束将读取到的字符给arr
    		fscanf(pf, "%s\n", arr);       //从空格位置开始读取跳过空格将后面的World读取到末尾结束将字符串给arr
    		printf("读取的内容为:%s\n", arr);
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    在这里插入图片描述

    ⑤.fread和fwrite二进制输入输出函数

    fread和fwrite 都是以二进制形式输入输出,输入到文件里显示的数据是二进制形式,具有一定的数据安全功能,因为很多人是读不懂二进制数的

    在这里插入图片描述

    函数fread()读取[num]个对象(每个对象大小为size(大小)指定的字节数),并把它们替换到由buffer(缓冲区)指定的数组. 数据来自给出的输入流. 函数的返回值是读取的内容数量…
    使用feof()或ferror()判断到底发生哪个错误

    在这里插入图片描述

    fwrite()函数从数组buffer(缓冲区)中, 写count个大小为size(大小)的对象到stream(流)指定的流. 返回值是已写的对象的数量.

    #define _CRT_SECURE_NO_WARNINGS
    #include
    int main()
    {
    	FILE* pf = fopen("text.txt", "wb");  //以二进制只写形式打开文件
    	if (pf == NULL)
    	{
    		perror("fopen");
    	}
    	else
    	{
    		int a=666666;  
    		int n=fwrite(&a, sizeof(a), 1, pf); //从 a里 读取1 个 sizeof(a)大小 的数据转换为二进制形式输出到pf指向的文件信息区中
    		if (n != 1)
    		{
    			printf("输出失败\n");
    		}
    		else
    		{
    			printf("输出成功\n");
    			fclose(pf);
    			pf = NULL;
    		}
    	}
    	 pf = fopen("text.txt", "rb");//以二进制只读形式打开文件
    	if (pf == NULL)
    	{
    		perror("fopen");
    	}
    	else
    	{
    		int num;
    		fread(&num, sizeof(num), 1, pf); //从文件信息中中读取 1个 sizeof(num)个大小的数据 放到num中 
    		printf("读取的数据为:%d", num);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    在这里插入图片描述

    从输出结果可以看出 以二进制形式将整形变量里的数输出到文件里,在文件里显示的数据是乱码,但只是在我们视角是乱码,在计算机内部表示的是666666 ,在最后以二进制形式读取到变量n中最后结果是666666

    2.文件的随机读写

    文件的随机读写,也是站在文件指针偏移量的角度来看,可以改变文件指针偏移量读取文件信息区中任意位置的数据

    ①fseek定位文件指针偏移量函数

    在这里插入图片描述

    函数fseek()为给出的流设置位置数据. origin的值应该是下列值其中之一(在stdio.h中定义):

    在这里插入图片描述
    SEEK_SET可以用数字0表示
    SEEK_CUR可以用数字1表示
    SEEK_END可以用数字2表示

    fseek()成功时返回0,失败时返回非零. 你可以使用fseek()移动超过一个文件,但是不能在开始处之前. 使用fseek()清除关联到流的EOF标记.

    fseek函数主要作用就是可以时当前指针偏移量变为从偏移量0位置移动n各单位 或者从当前偏移量位置移动n个单位 或者从文件末尾移动n个单位

    #define _CRT_SECURE_NO_WARNINGS
    #include
    int main()
    {
    	FILE* pf = fopen("text.txt", "w+"); //以读和写形式打开文件
    	if (pf == NULL)
    	{
    		perror("fopen");
    	}
    	else
    	{
    		fputc('a', pf);
    		fputc('b', pf);
    		fputc('c', pf);
    		char a = fgetc(pf);
    		printf("%c", a);//输出结果是什么?
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    运行三个fputc表示将abc输出到文件中,每输出一个字符文件指针偏移量往后移动一个位置,输出完c后文件指针偏移量移动到c后面,此时直接用fgetc读取的是文件指针偏移量处的字符,而此时偏移量处没有字符所以什么都没读取

    在这里插入图片描述

    此时可以用到fseek函数改变文件指针偏移量

    在这里插入图片描述
    将文件指针偏移量往左移动一个单位此时就到了指向c的位置,此时即可读取字符c

    在这里插入图片描述
    此时将文件指针偏移量从起始0位置往右移动1个位置就是b的位置,此时就可以读取b

    在这里插入图片描述
    此时表示从文件末尾往左移动-1个单位,文件末尾就是字符c后面的位置移动-1个单位就是c的位置 此时可以读取字符c

    ②.ftell获取当前文件指针偏移量函数

    在这里插入图片描述

    ftell函数 里面放文件指针,可以得到当前文件指针的偏移量并返回,即相对于起始地址偏移了多少个单位

    在这里插入图片描述

    可以看到经过最后读取完字符c后指针偏移量加1为了3,此时获取的文件指针偏移量为3

    ③.rewind重置文件指针偏移量函数

    在这里插入图片描述

    函数rewind()把文件指针移到由stream(流)指定的开始处, 同时清除和流相关的错误和EOF标记.

    不管文件指针偏移量是多少,使用此函数后会将指针偏移量变为0

    在这里插入图片描述

    可以看到使用了rewind函数后 文件指针偏移量为0了

    五.文本文件和二进制文件

    根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
    数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
    如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件

    #include 
    int main()
    {
    int a = 10000;
    FILE* pf = fopen("test.txt", "wb");
    fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
    fclose(pf);
    pf = NULL;
    return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    执行这串代码后在源程序目录里多出了text.txt文件 ,而此时里面的看不懂的字符,这正是二进制数据,通过二进制形式输出到文件中

    vs编辑器里有以二进制形式打开文件的方法↓
    在这里插入图片描述
    在这里插入图片描述
    此时用二进制形式打开文件后可以看出这个二进制文件表达的含义
    10 27 00 00 表示是在内存中的一串二进制数据以小端形式显示(数据低位放低地址,高位放高地址)转换为十进制数就是10000

    六.文件读取结束的判定

    在进行读取文件数据结束时可能有两种情况:
    1.读取到文件结束标准EOF结束读取
    2.读取文件过程中发生错误结束读取

    要区分实际读取结束是哪种情况需要feof和ferror函数判定

    1.feof判断文件读取末尾结束函数

    在这里插入图片描述

    函数feof()在到达给出的文件流的文件尾时返回一个非零值.
    当读取结束后可以用此函数可以判断文件指针是否在文件末尾结束,如果是返回非零值 如果不是返回零值

    2.ferror判断文件读取失败结束函数

    在这里插入图片描述

    ferror()函数检查stream(流)中的错误, 如果没发生错误返回0,否则返回非零. 如果发生错误, 使用perror()检测发生什么错误.

    #include 
    
    int main()
    {
    	int c; // 注意:int,非char,要求处理EOF
    	FILE* fp = fopen("test.txt", "r");
    	if (fp==NULL) {
    		perror("File opening failed");
    		
    	}
    	//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
    	while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
    	{
    		putchar(c);
    
    		
    		
    	}
    	//判断是什么原因结束的
    	if (ferror(fp))
    		puts("I/O error when reading"); //读取文件过程中失败
    	else if (feof(fp))
    		puts("End of file reached successfully"); //成功读取到文件末尾
    	fclose(fp);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述

    使用fgetc读取文件中123456时 读取过程失败或者读取到文件末尾结束标志时都会返回EOF
    当读取到EOF后结束循环,用feof和ferror分别判断是读取到末尾结束的还是读取失败结束的…

    牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。
    而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。

    1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets ) 例如: fgetc 判断是否为 EOF . fgets 判断返回值是否为 NULL .
    2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。 例如: fread判断返回值是否小于实际要读的个数

    七.了解文件缓冲区概念

    ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。
    从内存向磁盘输出数据会先送到内存中的缓冲区, 装满缓冲区后才一起送到磁盘上。
    如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),
    然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。
    缓冲区的大小根据C编译系统决定的。

    在这里插入图片描述

    文件缓冲区的使用就类似于老师上课过程中,不断有学生问问题,老师为了上课进度,不可能每有一个问题就停下来回答,而是会将问题留到下课后一一解答
    就好比缓存区,程序在运行中输入输出的数据都会先放到缓冲区中,非必要情况下,都是得缓冲区满或者程序结束前关闭 文件后才将数据输入输出到程序中,这样可以节省提高性能

    因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文 件。 如果不做,可能导致读写文件的问题。

    八.文件知识点总结

    本文介绍了基本常用的C语言文件定义,文件的基本操作(文件指针定义和文件的打开和关闭),文件的顺序读写(各种常用的文件读写函数),文件的随机读写(对指针偏移量操作的函数),文本文件和二进制文件.读取结束判定的函数(feof和ferror)文件缓冲区的概念
    在下篇博客,会使用常用的文件操场将学生信息管理系统改造为文件存储版本…

    在这里插入图片描述

  • 相关阅读:
    基于SpringBoot+MyBatisPlus+DynamicDatasource+mysql的多数据源本地事务方案
    Ubuntu 22.04 x86_64 llvm clang 16.0.6 源码编译安装
    物联网微消息队列MQTT介绍-EMQX集群搭建以及与SpringBoot整合
    Oracle/PLSQL: Bin_To_Num Function
    ios xcode 15 PrivacyInfo.xcprivacy 隐私清单
    js对象属性描述符
    java计算机毕业设计校友闲置书籍管理平台源代码+数据库+系统+lw文档
    java基于微信小程序的投票系统 uniapp 小程序
    Numpy:打开通往高效数值计算的大门
    AcWing 505. 火柴排队(每日一题)
  • 原文地址:https://blog.csdn.net/lch1552493370/article/details/127678585