【讨论】c语言判断文件结尾问题的刨根问底
本帖最后由 奉聪 于 2017-2-27 00:13 编辑一、问题的由来
最近在学习一些文件的知识,但有时会出现复制的文件会多出一个字节的问题
于是想是不是把所谓的 ‘文件末尾’ 也写了进去,于是找某度。。。
就牵涉到C语言怎么判断文件结尾的问题了:文件末尾到底有没有一个结束的标识?
二、寻找答案
答案一:
很多网友认为文本文件是通过一定编码规则再存入硬盘的,那么就不存在编码为-1的
(比如ASCⅡ码没有负数),那么EOF(-1)作为文本文件的结尾也是合理的。那么如
此说来二进制文件结尾又如何判断的呢? (二进制中-1可能是有效值)
答案二:
这位兄弟认为,不管是文本文件还是二进制文件,文件中根本不存在所谓的
‘文件末尾’标识。一切都是文件系统在管理。
只是某些函数(如fgetc)在读取到文本文件结束后会返回EOF(0XFFFFFFF)
(即整型的-1)
但是文件本身并无EOF这个标识符,貌似说的还是有道理哈,先信他
答案三:
文本文件有这个标识符,但是不是单纯的-1,而是Ctrl+z的ASCII码值0x1a
(有效值也可以为0x1a啊,这难道不是bug吗,exm?)
网友给出的依据是MSDN中关于fopen函数的一段话:
Open in text (translated) mode. In this mode, CTRL+Z is interpreted as an EOF character on input. In files that are opened
for reading/writing by using "a+", fopen checks for a CTRL+Z at the end of the file and removes it, if possible. This is done because
using fseek and ftell to move within a file that ends with CTRL+Z may cause fseek to behave incorrectly near the end of the file.
我也打开帮助文档,看到了这句话,官方翻译是:
在文本(转换)模式下打开。 在此模式下,CTRL+Z 将在输入时解释为 EOF 字符。 在使用 "a+" 打开以进行读取/写入
的文件中,fopen 将检查文件末尾的 CTRL+Z 并将其删除(如果可能)。 这是因为使用 fseek 和 ftell 在以 CTRL+Z 结尾的
文件中移动时,可能导致 fseek 在文件末尾附近错误运行。
于是我写了以下一小段代码进行测试
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void main()
{
FILE * pf = fopen("1.txt", "r");
char ch = 0;
int count = 0;
while (!feof(pf))//第四个为CTRL+Z的ASCII值 这里循环了四次
{
ch = fgetc(pf);
printf("%c---", ch);
count++;
}
printf("\n%d\n", count);
fclose(pf);
system("pause");
}
其中1.txt的内容是:
第四个字符我用16进制编辑器改成了0X1A:
输出结果
测试结果显示,feof函数也会认为Ctrl+z是文件结束的标志,而并非是比较文件
的长度来得到是否到达文件结尾(有些网友如是说),也就是说feof这个函数判
断是否结束,是和答案二中说的只根据文件结构信息来判断相矛盾了,这里很明
显判断了文本的内容。
后序我也测试了fgetc当遇到0x1a时,该函数返回的也是EOF。
但是问题又出现了,在16进制编辑的视图下,文件结尾并没有发现有0x1a
结尾又是怎么判断的呢?
继续寻找答案。。。
看来某度不能更好的解决我的疑问了,上某歌看看
三、继续追寻真理
翻了很多帖子和个人博客,终于在一位老外的博客中找到一段话
Do you see the problem yet? The function tests the end-of-file indicator, not the stream itself. This means that another
function is actually responsible for setting the indicator to denote EOF has been reached. This would normally be done by the
function that performed the read that hit EOF. We can then follow the problem to that function, and we find that most read
functions will set EOF once they've read all the data, and then performed a final read resulting in no data, only EOF.
With this in mind, how does it manifest itself into a bug in our snippet of code? Simple... as the program goes through the loop
to get the last line of data, fgets() works normally, without setting EOF, and we print out the data. The loop returns to the
top, and the call to feof() returns FALSE, and we start to go through the loop again. This time, the fgets() sees and sets EOF,
but thanks to our poor logic, we go on to process the buffer anyway, without realising that its content is now undefined (most
likely untouched from the last loop).
他的意思就是说呢feof并非检测当前文件内容是否是文件结束标识符,
而是检测一个标志,这个标志由其他函数设置,他这里提到了fgets函
数在获取内容为空后,会设置这个标志为TRUE,意思就是说就算指
针指向文件结尾,没有函数设置这个标志,feof就不会认为已经到了
文件结尾。那么我们就用下面代码做个小测试:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void main()
{
FILE * pf = fopen("1.txt", "r");
char ch = 0;
int count = 0;
fseek(pf, 3, SEEK_CUR);//3这个位置是0X1A
ch = fgetc(pf); //12行
printf("%c---", ch);//13行
if (feof(pf))
printf("end");
fclose(pf);
system("pause");
}
输出结果:
但是注释 12行 13行代码,end将不会被输出,也就证明了老外的结论。
好吧,我本想再找找更详细解答,但是网络有点问题,某歌打不开了,
明天还有几大生物学课程等着 我,还是睡了。
最后做一个我自己认识总结:(个人观点,欢迎提意见)
不管是二进制文件还是文本文件,文件本身并没有EOF这样一个标识符,
根据MSDN说的,只是0x1a在文本模式下打开,会被fgetc解释成文件结束,
并设置某一个标识变量。feof并非检测当前指针是否指向文件末尾或者判
断当前指向字符为结尾标识符,检测的是各种读取函数会设置的那个标识变量。
没人设置那个标识变量,那么他会一直认为不在文件结尾。
暂时先这样理解吧,后面有时间再深究,晚安了,明天一天可怕的各种生物学,想起摇头{:301_999:} 学而时习之,不亦乐乎! 厉害厉害厉害,楼主能推荐本学C语言的书吗? C语言确实很搞的:loveliness: 看来楼主很有分析问题的能力啊 将来定能成大事 夏雨微凉 发表于 2017-2-27 07:17
厉害厉害厉害,楼主能推荐本学C语言的书吗?
我买了那个 《The C programming language》 kuqideyanlei 发表于 2017-2-27 12:09
看来楼主很有分析问题的能力啊 将来定能成大事
哈哈哈,借您吉言 厉害厉害,居然分析的那么透切。 厉害,受教了。 233你似乎忘记了"rb"和"r"的区别
页:
[1]
2