使用 ftell 查找文件大小

Use ftell to find the file size

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

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

文件位置就像文本输入小部件中的光标:它们 文件的字节之间。如果我画一张图,这可能最容易理解:

这是一个假设的文件。它包含四个字符:abcd。每个角色都有自己的小盒子,我们称之为 "byte"。 (此文件为 ASCII。)第五个框已被划掉,因为它还不是文件的一部分,但如果您将第五个字符附加到文件,它将 spring 存在。

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

我不得不警告你,有几个原因

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

可能不会给你你真正想要的数字,这取决于你所说的 "file size" 的意思和文件的内容。排名不分先后:

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

  • long int 是 32 位的系统上,文件很容易比它大,在这种情况下 ftell 会失败,return -1 并设置errnoEOVERFLOW。 POSIX.1-2001 兼容系统提供了一个名为 ftello 的函数,如果您将 #define _FILE_OFFSET_BITS 64在所有源文件的最顶部(在任何 #include 之前)。我不知道 Windows 等价物是什么。

  • 如果您的文件包含超出 ASCII 的字符,那么文件中的 字节数 很可能与 [=文件中有 51=] 个字符 。 (例如,如果文件以 UTF-8 编码,字符 咖啡 将占用三个字节, Ä 将占用两个或三个字节取决于它是否是 "composed",而 జ్ఞా 将占用 十二 个字节,因为尽管是单个 grapheme ,它是一个由四个 Unicode 代码点组成的字符串。)ftell(o) 仍然会告诉您要传递给 malloc 的正确数字,如果您的目标是将整个文件读入内存,但遍历 "characters"不会像for (i = 0; i < len; i++).

  • 那么简单
  • 如果您使用 C 的 "wide streams" 和 "wide characters",那么,就像 Windows 上的文本流一样,您从 ftell 获得的数字该文件上的字节偏移量不是字节偏移量,除了随后调用 fseek 之外,可能对任何其他用途都没有用。但是宽流和字符无论如何都是一个糟糕的设计;如果您坚持在狭窄的流和字符中手动处理 UTF-8,您实际上更有可能正确处理世界上所有的语言。

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

不添加警告或警告是错误的,"This is how you get the number of bytes in a file." 因为当程序员第一次进入未将 fseek()/ftell() 定义为字节偏移量的系统时,他们会有问题。我看过了。

"But I was told this is how you can always do it."

"Well, no. Whoever taught you was wrong."

因为在strictly-conforming C代码中无法使用fseek()/ftell()获取文件的大小。

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

For a binary stream, the new position, measured in characters from the beginning of the file, is obtained by adding offset to the position specified by whence. The specified position is the beginning of the file if whence is SEEK_SET, the current value of the file position indicator if SEEK_CUR , or end-of-file if SEEK_END. A binary stream need not meaningfully support fseek calls with a whence value of SEEK_END.

脚注 268 特别指出:

Setting the file position indicator to end-of-file, as with fseek(file, 0, SEEK_END), has undefined behavior for a binary stream (because of possible trailing null characters) or for any stream with state-dependent encoding that does not assuredly end in the initial shift state.

因此您不能寻找二进制流的末尾来获取文件的字节大小。

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

The ftell function obtains the current value of the file position indicator for the stream pointed to by stream. For a binary stream, the value is the number of characters from the beginning of the file. For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call; the difference between two such return values is not necessarily a meaningful measure of the number of characters written or read.

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

据我所知,获取文件中字节数的唯一 strictly-conformant 方法是使用 fgetc() 读取它们 one-by-one 并计算它们。