文件操作相关知识总结

一,文件:磁盘上的文件是文件。但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件

1.程序文件:
包括源程序文件(后缀为.c), 目标文件(windows环境后缀为.obj), 可执行程序(windows环境后缀为.exe)。
通俗:你写了一个 .c 文件或者 .h文件 就是程序文件,这些文件可以‘被执行’或‘被编译’产生我们想要的东西
程序文件操作数据文件,可以 读 写 数据文件

2.数据文件:
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

二,文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件名包含3部分:

        文件路径 + 文件名主干 + 文件后缀
例如:  c : \code\    test        .txt

为了方便起见,文件标识常被称为文件名。
文件后缀:标识了文件的格式

三,文件类型
根据数据的组织形式,数据文件被称为‘文本文件’或者‘二进制文件’。

1.数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
通俗:我们肉眼可以看懂的文件
例如:我们写的代码,我们写的代码我们用记事本打开,我们能够看得懂他

2.如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
通俗:我们肉眼可以看不懂的文件
例如:。obj 文件,我们用记事本打开我们根本看不懂

3.一个数据在内存中是怎么存储的呢?
字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输
出,则在磁盘上只占4个字节(VS2013测试)。
例如:

//10000
//00000000 00000000 00100111 00010000二进制表达
//
//ASCLL形式 00110001 00110000 00110000 00110000 00110000
//            49      48        48       48      48
//
//二进制 00000000 00000000 00100111 00010000

测试代码:

//#include 
//int main()
//{
//	int a = 10000;//创建一个整型值10000
//	FILE* pf = fopen("test.txt", "wb");
//	//      打开一个文件 叫“t...” 以二进制的形式写这个a写进去
//	//当这个文件不存在时,会创建一个这样的文件
//	fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
//	//fwrite写文件
//	//&a  表示内容来自a这里
//	//4   写四个字节
//	//1   写一个这样的数据
//	//pf  放到这里pf维护的这个文件里去
//	//
//	fclose(pf);
//	//关闭这个文件
//	pf = NULL;
//	//关闭完,让他变成空指针
//	return 0;
//}

在外部打开今天写的代码–>继续打开今天的代码–>test.txt
在外部用任何东西打开 test.txt 你都看不懂
只有vs自己可以看的懂,
此时:视图->解决方案资源管理器->源文件->添加->现有项(把test.txt 添加进去)
右键test.txt ->打开方式->选择(二进制编辑器)->确定
此时便会打开 test.txt
里面显示00000000 10 27 00 00
读法:00 00 27 10(这是十六进制)
换算一下,换成十进制的,此时就是10000
虽然这个是十六进制,但是在内存中的存储还是二进制
将一个整数以二进制的方式存储到文件里面去,此时他就是二进制文件

如果以文本形式存储进去,那么他就是文本文件
文本文件就是10000存储进去
文本文件存储:直接在外面打开这个文件 test.txt 然后将里面的内容变为10000

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

//           ->输出缓冲区\
//	     /              -- - 输出--->
//程序数据区                          磁盘
//          \            <---输入---/
//		  ->输入缓冲区/

2.操作解释
在虚拟机中输入下面代码

//int main()
//{ 
//	while (1)
//	{
//		printf("hehe");
//	}
//	return 0;
//}

结果:一直输出hehehehehe…
接下来输入这段代码:

//#include
//int main()
//{ 
//	while (1)
//	{ 
//		Sleep(1);
//		printf("hehe");
//	}
//	return 0;
//}

然后运行
此时不会和第一次一样出现输出结果
推断:你每间隔一毫秒时间输入进去一个hehe,需要将输入缓冲区塞满,才会正常运行
代码运行结果:运行后,间隔一段时间后,屏幕会出现一大堆hehe…然后在等一段时间,又会出现一大堆hehe…
原因:输入缓冲区是有大小的
图形解释:

//               输入缓冲区
//printf--输出-->hehe......--->屏幕

五。文件指针
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE…
通俗解释:
假如,你写了一个文件 test.txt ,然后你想运行这个文件,此时便会开辟一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息会保存在一个struct _iobuf的结构体变量里面系统将他命名为FILE,每次用这些信息时,就通过FILE这个指针来寻找相应的信息。(仅个人对文件指针的理解,仅供参考,如有不妥,请指正)

对于FILE:右键FILE点击转到定义,里面会有这样的一段代码

//#ifndef _FILE_DEFINED
//struct _iobuf {
//	char *_ptr;
//	int   _cnt;
//	char *_base;
//	int   _flag;
//	int   _file;
//	int   _charbuf;
//	int   _bufsiz;
//	char *_tmpfname;
//};
//typedef struct _iobuf FILE;
// 将struct _iobuf 这个结构体起名为FILE

六,文件的打开与关闭
文件在读写之前应该先‘打开文件’,在使用结束之后应该‘关闭文件’。
FILE * fopen(const char * filename, const char * mode);
打开文件用的是fopen
char是一个字符指针
filename是文件名
引文文件传过去传的是受地址,所以是char

mode是打开模式方式

int fclose(FILE * stream);
关闭文件

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

举例:
“r”(只读) 为了输入数据,打开一个已经存在的文本文件 出错
下面为测试代码,先检查一下写的文件是否存在,防止重复(刚才已经创建了一个文件,防止相互干扰)

//int main()
//{
//	//打开文件 
//	//相对路径的写法
//	fopen("test.txt","r");	
//	//.. 表示上一级路径
//	//.  表示当前路径
//	//举例:test.txt这个文件的上一个路径也有一个test.txt如果想打开这个文件则需要输入下面代码
//	//fopen("../test.txt","r");
//	//如果还想写这个上一个路径(即第一个的上上路径)
//	//fopen("../../test.txt","r");
//
//
//	//文件有很多,为了防止重名,可以用 
//	//绝对路径的写法
//	//例如:
//	fopen("C:\\Users\\wyw15\\Desktop\\新建代码1\\test5_12\\test5_12\\test.txt", "r");
//	return 0;
//}

延申:strerror(errno)需要引用两个头文件 #include #include

//int main()
//{
//	//FILE* pf = fopen("tesrt.txt", "r");
//	//fopen("tesrt.txt", "r")打开文件会返回一个指针,我们用pf接收他
//	//当然可能会打开成功,也会打开失败,如果打开失败,则会返回一个NULL
//
//	//这个会打开或创建一个文件(test.txt)并在这个文件中的内存中开头创建一个文件信息区
//	//这个文件信息区里面放的就是这个文件的相关信息,这个能够更好的管理这个文件(test.txt)
//	//它的类型就是FILE的一种结构体变量,他的起始位置就会按照指针的形式返回即pf,
//	
//	FILE* pf = fopen("test.txt", "r");
//	//打开失败
//	if (pf == NULL)
//	{
//		printf("%s\n",strerror(errno));
//		//strerror(errno)这个能够获取这个错误代码的错误信息
//		//printf("%s\n",strerror(errno));这样可以把这个信息打印出来 
//		return 0;
//	}
//	//打开成功
//	//读文件
//	//关闭文件
//	fclose(pf);
//	pf = NULL;
//	return 0;
//	//因为你是以'读的形式'(r)打开,因为没有这个文件所以会打开失败
//	//出现:No such file or directory
//
//	//当你创建一个文件 test.txt 时,此时你运行代码
//	//没有任何问题
//}
//int main()
//{
//	FILE* pf = fopen("test.txt", "w");
//	if (pf == NULL)
//	{
//		printf("%s\n",strerror(errno)); 
//		return 0;
//	}
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}

如果你以写的形式‘w’打开,即使不创建文件test.txt也会没有任何问题,他会自动生成一个文件test.txt
详情请参考上面‘打开方式如下:(mode)’表
如果此时你在外面打开这个文件test.txt并往里面放一个信息(abcdef)(随便)然后运行这个代码,
然后在外面打开这个文件,会发现这个文件里面的内容会消失不见
即:你以写的形式打开旧文件,会创建一个新文件,并覆盖这个文件
详情请参考上面‘打开方式如下:(mode)’表

七,文件的顺序读写

//功能               函数名       适用于
//字符输入函数       fgetc        所有输入流
//字符输出函数       fputc        所有输出流
//文本行输入函数     fgets        所有输入流
//文本行输出函数     fputs        所有输出流
//格式化输入函数     fscanf       所有输入流
//格式化输出函数     fprintf      所有输出流
//二进制输入         fread        文件
//二进制输出         fwrite       文件
//这些可以用在msdn中查找,里面有相关知识

延申:
从键盘输入
输出到屏幕.
键盘&屏幕都是外部设备
我们可以 读写 外部设备

键盘-标准输入设备- stdin
屏幕-标准输出设备- stdout
是一个程序默认打开的两个流设备
只要我们的程序运行起来,就会默认打开三个 流

//stdin FILE*
//stdout FILE*
//stderr FILE*
//类型都是FILE


//功能               函数名       适用于
//字符输入函数       fgetc        所有输入流
//字符输出函数       fputc        所有输出流
//文本行输入函数     fgets        所有输入流
//文本行输出函数     fputs        所有输出流
//格式化输入函数     fscanf       所有输入流
//格式化输出函数     fprintf      所有输出流
//这里的fgetc fputc...适用于所有流
//int main()
//{
//	int ch = fgetc(stdin);
//	//fgetc读取一个字符,从stdin流里面读取,并存储到ch里面
//	fputc(ch, stdout);
//	//再把ch打印出来
//	//从键盘输入,从屏幕读
//	return 0;
//}

程序运行:你从键盘随便输入一个字符或者数字,enter。屏幕会出现你输入的字符或数字
此时你可以发现fgetc可以从标准输入流输入,从标准输出流输出
即:从键盘输入,从屏幕看到

举例:

//写文件
//int main()
//{
//	FILE* pf = fopen("test.txt", "w");
//	if (pf == NULL)
//	{
//		printf("%s\n", strerror(errno));
//		return 0;
//	}
//	//写文件
//	fputc('a', pf);
//	fputc('b', pf);
//	fputc('c', pf);
//
//	//关闭文件
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//读文件
//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		printf("%s\n", strerror(errno));
//		return 0;
//	}
//	//读文件
//	int ch = fgetc(pf);//fgetc只能读一个
//	printf("%c", ch);
//	ch = fgetc(pf);
//	printf("%c", ch);
//	ch = fgetc(pf);
//	printf("%c", ch);
//
//	//关闭文件
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//读文件
//此时在外部打开test.txt,在第二行输入一行字符(随便),例如:hello
//int main()
//{
//	char buf[1024] = { 0 };//创建一个临时数组
//
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		return 0;
//	}
//	//读文件
//	fgets(buf, 1024, pf);
//	printf("%s\n", buf);
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//输出:
//abc
//
//请按任意键继续.....
//buf里面自带一个换行,所以不用加\n
//解释:我们在文件里面存储时,存储的就是abc\n,当他读取时,也把\n读取了,所以换行了
//      我们自己在文件里面写的hello没有写\n,所以读取时,不读\n
//一次只能读取一行,所以我们用两次
//读文件
//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		printf("%s\n", strerror(errno));
//		return 0;
//	}
//	//读文件
//	int ch = fgetc(pf);//fgetc只能读一个
//	printf("%c", ch);
//	ch = fgetc(pf);
//	printf("%c", ch);
//	ch = fgetc(pf);
//	printf("%c", ch);
//
//	//关闭文件
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//读文件
//此时在外部打开test.txt,在第二行输入一行字符(随便),例如:hello
//int main()
//{
//	char buf[1024] = { 0 };//创建一个临时数组
//
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		return 0;
//	}
//	//读文件
//	fgets(buf, 1024, pf);
//	printf("%s\n", buf);
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//输出:
//abc
//
//请按任意键继续.....
//buf里面自带一个换行,所以不用加\n
//解释:我们在文件里面存储时,存储的就是abc\n,当他读取时,也把\n读取了,所以换行了
//      我们自己在文件里面写的hello没有写\n,所以读取时,不读\n
//一次只能读取一行,所以我们用两次
//int main()
//{
//	char buf[1024] = { 0 };//创建一个临时数组
//
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		return 0;
//	}
//	//读文件
//	fgets(buf, 1024, pf);
//	//printf("%s", buf);
//	puts(buf);
//
//	fgets(buf, 1024, pf);
//	//printf("%s", buf);
//	puts(buf);
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//fgets(..1......,....2....,...3.....);
//读取以3为地址的内容,将其放到1里面,读取 2内容(数字) 大小的内容
//int main()
//{
//	char buf[1024] = { 0 };
//
//	FILE* pf = fopen("test.txt", "w");
//	if (pf == NULL)
//	{
//		return 0;
//	}
//	//写文件
//	fputs("hello\n", pf);
//	fputs("world\n", pf);
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//注意:fputs这个写的话如果要换行,自己要写\n,不然就是一行字符
//输入并读取字符串
//int main()
//{
//	//从键盘读取一行文本信息
//	//char buf[1024] = {0};
//	fgets(buf, 1024, stdin);//从标准输入流读取
//	fputs(buf, stdout);//输出到标准输出流
//
//	//等价于
//
//	//gets(buf);
//	//puts(buf);
//
//	return 0;
//}
//输入结构体
//struct S
//{
//	int n;
//	float score;
//	char arr[10];
//};
//
//int main()
//{
//	struct S s = { 100, 3.14f, "abc" };
//	FILE* pf = fopen("test.txt", "w");
//	if (pf == NULL)
//	{
//		return 0;
//	}
//	//格式化的形式写文件
//	fprintf(pf, "%d %f %s", s.n, s.score, s.arr);
//
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}

//读取结构体
//struct S
//{
//	int n;
//	float score;
//	char arr[10];
//};
//
//int main()
//{
//	struct S s = {0};
//
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		return 0;
//	}
//	//格式化的输入数据
//	fscanf(pf, "%d %f %s", &(s.n), &(s.score), s.arr);
//	printf("%d %f %s\n", s.n, s.score, s.arr);
//
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}

//struct S
//{
//	int n;
//	float score;
//	char arr[10];
//};
//
//int main()
//{
//	struct S s = { 0 };
//
//	fscanf(stdin, "%d %f %s", &(s.n), &(s.score), s.arr);
//	fprintf(stdout, "%d %.2f %s", s.n, s.score, s.arr);
//
//	return 0;
//}
// %.2f 可以指定读取小数点后几位

对比函数
scanf/fscanf/sscanf
printf/fprintf/sprintf

scanf/printf 是针对标准输入流/标准输出流的 格式化输入/输出语句
fscanf/fprintf 是针对所有输入流/所有输出流的 格式化输入/输出语句
因为所有所以fscanf/fprintf包含scanf/printf

sscanf/sprintf
sscanf/swscanf
scanf 从一个字符串里面读取一个格式化的数据(read formatted data from a string)

sprintf/swprintf
写格式化的数据到一个字符串里面

//struct S
//{
//	int n;
//	float score;
//	char arr[10];
//};
//
//int main()
//{
//	struct S s = { 100, 3.14f, "abcdef" };
//	//将s里面的数据转换为字符串 
//	char buf[1024] = { 0 };
//
//	sprintf(buf, "%d %f %s", s.n, s.score, s.arr);
//	printf("%s\n", buf);
//	//输出:100 3.140000 abcdef
//	//我们以%s的形式输出,因为能输出,所以我们成功把他转换为字符串
//	//即sprintf有能力把结构体里面的数据转换为字符串 
//	return 0;
//}

//struct S
//{
//	int n;
//	float score;
//	char arr[10];
//};
//
//int main()
//{
//	
//	struct S s = { 100, 3.14f, "abcdef" };
//	struct S tmp = { 0 };//新建结构体
//	char buf[1024] = { 0 };
//	//我们从buf里面提取一个结构体,将上一个代码翻译回去 
//	//上一个代码,我们将结构体翻译成字符串,此时我们把字符串翻译回去
//
//	//把格式化的数据转换成字符串存储到buf
//	sprintf(buf, "%d %f %s", s.n, s.score, s.arr);
//	//printf("%s\n", buf);
//
//	//从buf中读取格式化的数据到tmp中
//	sscanf(buf, "%d %f %s", &(tmp.n), &(tmp.score), tmp.arr);
//
//	printf("%d %f %s\n", tmp.n, tmp.score, tmp.arr);
//	//输出结果:100 3.140000 abcdef
//	
//
//	return 0;
//}
//sscanf/sprintf
//sscanf是从字符串中读取格式化的数据
//sprintf是把格式化数据输出成(存储到)字符串

二进制的输入与输出

//二进制的输入
//struct S
//{
//
//    char name[20];
//    int age;
//    double score;
//};
//
//int main()
//{
//     struct S s = { "张三", 20, 55.6 };
//
//     FILE* pf = fopen("test.txt", "wb");
//     if (pf == NULL)
//     {
//         return 0;
//     }
//     //二进制的形式写文件
//     fwrite(&s, sizeof(struct S), 1, pf);
//           读取地址  读取大小  读取个数 读取到  
//     fclose(pf);
//     pf = NULL;
//     return 0;
//}
//此时运行程序,在外部打开test.txt文件,会出现:张三。。。(不认识的)
//此时我们便已经把内容以二进制的形式写进文件里面了

//二进制的输出
//struct S
//{
//     char name[20];
//     int age;
//     double score;
//};
//
//int main()
//{
//     //struct S s = { "张三", 20, 55.6 };
//     struct S tmp = { 0 };
//
//     FILE* pf = fopen("test.txt", "rb");
//     if (pf == NULL)
//    {
//         return 0;
//     }
//     //二进制的形式都文件
//     fread(&tmp, sizeof(struct S), 1, pf);
//     printf("%s %d %lf\n", tmp.name, tmp.age, tmp.score);
//
//     fclose(pf);
//     pf = NULL;
//     return 0;
//}
//运行代码,输出 20 55.600000
//虽然外部文件以二进制的存储我们看不懂,但是内部运行输出没有任何问题

八,文件的随机读写(我用具体代码进行总结)

//我们在外部将文件内容改为:abcdef
//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		return 0;
//	}
//	//1.定位文件指针
//	fseek(pf, -2, SEEK_END);
//	//fseek:根据文件指针的位置和偏移量来定位文件指针。
//	//pf:相关的指针
//	//2:偏移量 (可以为负数)
//	//SEEK_END:文件指针的当前位置
//	
//	//SEEK_CUR文件指针的当前位置;
//	//SEEK_END文件的末尾位置;(此时指针在\0前面)
//	//SEEK_SET文件起始位置;
//	//调整pf指向的文件指针 以(文件指针的当前位置)为起点进行偏移 偏移(偏移量)
//
//	//2.读取文件
//	int ch = fgetc(pf);
//	printf("%c\n", ch);
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}

//ftell:返回文件指针相对于起始位置的偏移量
//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		return 0;
//	}
//	//1.定位文件指针
//	fseek(pf, -2, SEEK_END);
//	int pos = ftell(pf);
//	printf("%d\n", pos);
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//输出:4
//原因:我们在外部的文件内容是abcdef一共六个fseek(pf, -2, SEEK_END)相当于从后往前数两个即:6-2=4

//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		return 0;
//	}
//	//1.定位文件指针
//	//fseek(pf, -2, SEEK_END);
//	int pos = ftell(pf);
//	printf("%d\n", pos);
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//此时我们不定位文件指针,默认打开文件的位置是起始位置
//所以输出:0

//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		return 0;
//	}
//	//1.定位文件指针
//	//fseek(pf, -2, SEEK_END);
//	fgetc(pf);
//
//	int pos = ftell(pf);
//	printf("%d\n", pos);
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//此时我们读取一个,此时文件指针会向后面偏移一个
//所以输出为:1

//rewind:让文件指针的位置回到文件的起始位置
//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		return 0;
//	}
//	int ch = fgetc(pf);//此时我们读取一个,此时文件指针会向后面偏移一个
//	printf("%c\n", ch);
//
//	rewind(pf);//将指针的位置返回至起始位置
//	ch = fgetc(pf);
//	printf("%c\n", ch);
//
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}
//输出:a
//      a

九,文件结束判定feof

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

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

延申:

//int main()
//{
//	//EOF  转到定义会发现其值为-1
//	//feof();//EOF - end of file - 文件结束标志
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//		return 0;
//	int ch = fgetc(pf);
//	printf("%d\n", ch);//-1
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//此时我们将外部文件test.txt里面内容改为:(空)
//运行代码(输出外部文件里面的内容):-1

//延申perror(不需要引用头文件)
//int main()
//{
//	//strerror - 把错误码对应的错误信息的字符串地址返回
//	//printf("%s\n", strerror(errno));
//	
//	//perror
//	FILE* pf = fopen("test2.txt", "r");//test2.txt文件不存在
//	if (pf == NULL)
//	{
//		perror("open file test2.txt");
//		return 0;
//	}
//	//读文件
//  //。。。。。	
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}
//输出:open file test2.txt:No such file or directory(容易发现具体错误)
//(完全可以替代strerror(errno)

正确使用:

//外部文件test.txt内容改为:abcdef
//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("open file test2.txt");
//		return 0;
//	}
//	//读文件
//	int ch = 0;
//	while ((ch = fgetc(pf)) != EOF)//读取内容	
//	{
//		putchar(ch);
//	}
//	if (ferror(pf))//判断结束的原因
//	{
//		printf("error\n");
//	}
//	else if (feof(pf))
//	{
//		printf("end of file\n");
//	}
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}

你可能感兴趣的:(c++)