使用ftell查找文件大小

时间:2018-03-06 00:33:14

标签: c ftell

  fseek(f, 0, SEEK_END); 
  size = ftell(f);

如果ftell(f)告诉我们当前的文件位置,这里的大小应该是从文件末尾到开头的偏移量。为什么尺寸不是ftell(f)+1?不应该ftell(f)只给我们文件末尾的位置吗?

2 个答案:

答案 0 :(得分:4)

文件位置类似于文本输入窗口小部件中的光标:它们位于之间文件的字节。如果我画一幅画,这可能是最容易理解的:

Depiction of a file that is four characters long. There are five boxes in a row; from left to right, they contain the letters "a", "b", "c", and "d", and the fifth one has its area X-ed out.  Below the boxes, aligned with the vertical lines to the left of and in between the boxes, are the numbers 0, 1, 2, 3, and 4.

这是一个假设的文件。它包含四个字符: a b c d 。每个角色都有一个小盒子,我们称之为“字节”。 (这个文件是ASCII。)第五个框已被删除,因为它还不是文件的一部分,但是如果你在文件中附加了第五个字符,它就会出现。

此文件中的有效文件位置为0,1,2,3和4.其中有五个,而不是四个;它们对应于方框之前,之后和之间的垂直线。当您打开文件时(假设您不使用"a"),从位置0开始,即文件中第一个字节之前的行。当你寻找到最后,你到达位置4,即文件中最后一个字节后面的行。因为我们从零开始计数,所以这也是文件中的字节数。 (这是为什么我们从零开始计数而不是一个计数的几个原因之一。)

我有义务警告你,有几个原因

fseek(fp, 0, SEEK_END);
long int nbytes = ftell(fp);

可能无法提供您实际需要的数字,具体取决于您对“文件大小”的含义以及文件内容的含义。没有特别的顺序:

  • 在Windows上,如果您以文本模式打开文件,则从该文件ftell获取的数字与文件开头的不是字节偏移量;它们更像fgetpos个Cookie,只能用于后续调用fseek。如果你需要在Windows上的文本文件中寻找,你可能最好以二进制模式打开文件并自己处理DOS和Unix行结尾 - 这实际上是我对生产代码的一般推荐,因为它完全可以在Unix系统上有一个带有DOS行结尾的文件,反之亦然。

  • long int为32位的系统上,文件很容易大于此值,在这种情况下ftell将失败,返回-1并将errno设置为{{ 1}}。符合POSIX.1-2001标准的系统提供了一个名为ftello的函数,如果您将EOVERFLOW放在所有源文件的最顶层,则返回off_t数量可以代表更大的文件大小(在任何#define _FILE_OFFSET_BITS 64之前)。我不知道Windows的等价物是什么。

  • 如果您的文件包含超出ASCII的字符,则文件中 bytes 的数量很可能与字符的数量不同文件。 (例如,如果文件以UTF-8编码,则字符将占用三个字节,Ä将占用两个或三个字节,具体取决于它是否为“组成”,<్ఞా将占用 12个字节,因为尽管是单个grapheme,但它是一个由四个Unicode代码点组成的字符串。){{1如果您的目标是将整个文件读入内存,但仍然会告诉您传递给#include的正确数字,但迭代“字符”不会像ftell(o)那样简单。

  • 如果您正在使用C的“宽流”和“宽字符”,那么,就像Windows上的文本流一样,您从该文件malloc获得的数字不是字节偏移,也可能不是除了后续调用for (i = 0; i < len; i++)之外的其他任何内容都有用。但无论如何,广泛的流和角色都是糟糕的设计;如果你坚持在狭窄的流和角色中手工处理UTF-8,你实际上更有可能正确处理世界上所有的语言。

答案 1 :(得分:1)

我不确定为什么fseek() / ftell()被教导为获取文件大小的通用方法。它只能起作用,因为实现将其定义为可行。 POSIX确实如此。 Windows也适用于二进制流 - 但不适用于文本流。

不添加警告或警告,“这就是你获取文件中字节数的方法”。因为当程序员首次进入一个没有将fseek() / ftell()定义为字节偏移的系统时,它们会遇到问题。我已经看过了。

“但我被告知这是你总能做到的。”

“好吧,没有。教过你的人错了。”

因为无法使用fseek() / ftell()来获取严格符合C代码的文件大小。

对于二进制流, 7.21.9.2 fseek函数the C standard的第3段:

  

对于二进制流,新位置,以字符为单位   通过将offset添加到文件的开头来获取文件的开头   由whence指定的位置。指定的位置是   如果whenceSEEK_SET,则该文件的开头是当前值   文件位置指示符,如果SEEK_CUR,或文件结束if   SEEK_END二进制流无需有意义地支持fseek   whence值为SEEK_END的来电。

脚注268明确指出:

  

将文件位置指示器设置为文件结尾,如同   fseek(file, 0, SEEK_END)对二进制文件有未定义的行为   流(因为可能是尾随空字符)或任何   具有状态依赖编码的流,但不能确定地结束   初始转变状态。

因此,您无法寻找二进制流的结尾来获取文件的大小(以字节为单位)。

对于文本流, 7.21.9.4 ftell函数,第2段指出:

  

ftell函数获取文件位置的当前值   stream指向的流的指示符。对于二进制文件   stream,该值是来自的字符数   文件的开头。 对于文本流,其文件位置   指标包含未指定的信息,可由fseek使用   用于将流的文件位置指示符返回到的函数   它在ftell电话时的位置; 差异   两个这样的返回值之间不一定有意义   衡量写作或阅读的字符数

因此,您无法在文本流上使用ftell()来获取字节数。

我知道获取文件中字节数的唯一严格一致的方法是使用fgetc()逐个读取它们并计算它们。