关于使用fgetc函数和feof函数的一些注意事项
众所周知,文件分为二进制文件和文本文件,它们在磁盘中的存储方式是相同的,都是二进制存储,但是它们的读取方式不同。对于文本文件,我们通常使用逐个字节读取的方式,这是因为文本文件在磁盘中的存储方式就是将每个字符转换为对应的ASCII码进行存储的,而二进制文件的读取方式根据不同的应用场景也不相同,有可能先读取前4个字节,再读取后面2个字节,也有可能先读取前面2个字节,再读取后面4个字节。
如果大家理解了它们读取方式的不同,对于fgetc和feof函数就会有进一步的理解。首先我们来看看fgetc函数,下面是从C++官网上截取的关于fgetc函数的介绍。
从这个介绍中我们可以获取两个重要的知识点:
(1)它的返回值是int类型,而不是char类型;
(2)如果文件的流指针在文件末尾或者读取数据发生错误时,fgetc函数会返回EOF,也就是-1。
这个函数的工作原理是这样的,它会根据FILE*指针对应的文件流指针指定的位置,向前读取一个字节的内容,请注意它会将这个字节作为unsigned char类型的数据,当将其提升为int类型数据返回时,根据整型提升原则,unsigned char提升为int类型的数据,前面全补0。这也就是网上很多博主说的,如果fgetc函数读取到0xFF这个字节,会返回0x000000FF的int类型数据,而不是返回0xFFFFFFFF,也就是-1。我们可以写个代码来验证一下:
通过上面的代码,我们将int类型的变量a对应的二进制,也就是0xffffffff写入test.txt文件中,此时test.txt文件的内容有4个字节,每个字节都是0xFF。从下图可以看到结果:
然后我们使用fgetc函数读取test.txt文件。
上面的结果验证了我们刚才的结论,也这就是网上许多人认为必须要用int类型的变量去存放fgetc函数的返回值,如果在读取二进制文件中,很有可能读取到0xff字节,此时如果用char类型的变量存放fgetc函数的返回值,会发生数据截取,导致char类型变量的内容为0xFF,将它和EOF做是否相等判断就会成立。
但是我想说的是,fgetc函数根本不能在二进制文件的读取中使用,因为二进制文件中存储的数据根本不是按照逐字节的方式存放的,使用fgetc函数读取的内容毫无意义,因此fgetc函数既不适用于读取二进制文件,也不适用于判断二进制文件的结束。只有在文本文件的读取中,我们才能使用fgetc函数,我认为网上讨论的使用int类型变量存放该函数的返回值而不能使用char类型变量存放函数返回值是没有任何意义的。
如果在文本文件的读取中使用fgetc函数,由于不存在字符对应0xff,我们既可以使用int类型变量存放该函数的返回值也可以使用char类型变量存放函数返回值。在这里我想纠正的是:当我们使用fgetc函数获取到EOF返回值时,并不能说明读取到文件末尾了,因为该函数的介绍也说了,如果读取数据错误时,该函数也会返回EOF。只不过我们平时在测试代码中不太会遇到读取数据错误,因此大家都认为当使用fgetc函数返回一个EOF时,就意味着读取到文件末尾了。这时,肯定很多人就会问:如何才能判断文本文件的末尾呢?这就需要feof函数的搭配使用了。
对于feof函数大家都认为这个函数是判断文件结尾的函数,其实严格意义上来讲它的真正作用是要和fgetc或者fread函数结合使用,判断是否读取文件结束。
我们新建一个test.txt文本文件,里面存放字符串abcdef,如下图:
再运行如下代码:
可以看到,虽然文件的流指针已经到达文件末尾了,但是使用feof函数后,返回值依然是0,也就意味着没有到达末尾,这是为什么呢?虽然文件流指针已经到了文件末尾,但是feof函数认为后面依然有内容,所以不会认为是文件末尾,才会返回0。如果我们再使用一次fgetc函数,因为已经到了文件末尾,就会返回EOF,此时函数会将这个文件对应的FILE*指针指向的文件信息修改,这时再使用feof函数,就会返回1。
综上所述,我认为feof函数单独使用没有实际意义,针对于文本文件的操作来说,需要配合fgetc函数或者其他读取函数使用,当我们使用读取函数读取文本文件时,如果返回EOF,可以调用feof函数,来判断返回EOF的原因,如果feof返回1,则说明是因为读取到文件末尾导致的返回EOF,如果feof返回0,表示是因为中间读取数据错误导致的返回EOF。
写到这里,对于文本文件就算是介绍完毕了,那么如何判断二进制文件呢?一般来说,二进制文件有其特定的读取方式,除了开发者其他人员都不会知道到底如何读取,一般使用fread函数来读取不同长度的数据。我们可以根据fread函数的返回值来判断读取是否出现错误。
上面是fread函数的原型,如果函数的返回值小于函数参数中读取的数据个数,我们就认为此次读取出现错误,原因有两种:一种是中间读取数据失败,另一种是读取到文件末尾,已经没有数据可读了。那么如何判断是哪种原因呢,就需要feof函数,如果返回值为1,表示已经读取到文件末尾,反之则表示中间读取失败。
最后声明下,这只是个人理解,毕竟和资深的IT大佬无法相比,有什么不同意见或者理解,欢迎留言讨论。