5

在 C 或 C++ 中,以下可用于返回文件大小:

const unsigned long long at_beg = (unsigned long long) ftell(filePtr);
fseek(filePtr, 0, SEEK_END);
const unsigned long long at_end = (unsigned long long) ftell(filePtr);
const unsigned long long length_in_bytes = at_end - at_beg;
fprintf(stdout, "file size: %llu\n", length_in_bytes);

是否有开发环境、编译器或操作系统可以根据填充或其他特定于情况的信息从该代码返回错误的文件大小?1999 年左右 C 或 C++ 规范是否发生了变化,这会导致该代码在某些情况下不再工作?

对于这个问题,请假设我通过使用 flags 编译来添加大文件支持-D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE=1。谢谢。

4

4 回答 4

7

它不适用于不可搜索的文件,如/proc/cpuinfoor/dev/stdin/dev/tty,或管道文件popen

如果该文件同时由另一个进程写入,它将不起作用。

使用Posix stat函数可能更有效、更可靠。当然,此功能可能在非 Posix 系统上不可用。

于 2012-02-03T20:59:39.663 回答
4

和函数均由 ISO C 语言标准定义fseekftell

以下是2011 C 标准的最新公开草案,但 1990、1999 和 2011 ISO C 标准在这方面都非常相似,如果不完全相同的话。

7.21.9.4:

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

7.21.9.2:

fseek函数为 stream 指向的流设置文件位置指示符。如果发生读取或写入错误,则设置流的错误指示符并且fseek失败。

对于二进制流,新位置(以文件开头的字符为单位)是通过将offset添加到whence指定的位置来获得的。如果whereceSEEK_SET ,则指定位置是文件的开头,如果是 SEEK_CUR,则为文件位置指示符的当前值,如果是SEEK_END ,则为文件结尾 。二进制流不需要有意义地支持wherece值为SEEK_END的fseek调用。

对于文本流,偏移量应为零,或偏移量 应为先前成功调用 与同一文件关联的流上的ftell函数返回的值,并且应为SEEK_SET

违反任何“shall”子句会使您的程序的行为未定义。

因此,如果文件以二进制模式打开,则为ftell您提供从文件开头开始的字符数——但fseek相对于文件结尾 ( SEEK_END) 不一定有意义。这适用于将二进制文件存储在整个块中并且不跟踪写入最终块的数量的系统。

如果文件以文本模式打开,您可以使用偏移量 0 查找文件的开头或结尾,或者您可以查找之前调用ftell;给出的位置。fseek与任何其他参数有未定义的行为。这适用于从文本文件中读取的字符数不一定与文件中的字节数相对应的系统。例如,在 Windows 上读取 CR-LF 对 ( "\r\n") 只会读取一个字符,但会在文件中前进 2 个字节。

实际上,在类 Unix 系统上,文本和二进制模式的行为方式相同,并且 fseek/ftell 方法将起作用。我怀疑它可以在 Windows 上运行(我的猜测是它ftell会给出字节偏移量,这可能与您getchar()在文本模式下调用的次数不同)。

另请注意,ftell()返回类型的结果long。在long32 位系统上,此方法不适用于 2 GiB 或更大的文件。

您最好使用一些特定于系统的方法来获取文件的大小。因为 fseek/ftell 方法无论如何都是系统特定的,例如stat()在类 Unix 系统上。

另一方面,fseek并​​且可能在您可能遇到的大多数ftell系统上按预期工作。我敢肯定有些系统不起作用。对不起,但我没有具体细节。

如果在 Linux 和 Windows 上工作足够好,并且您不关心大文件,那么 fseek/ftell 方法可能没问题。否则,您应该考虑使用特定于系统的方法来确定文件的大小。

请记住,任何告诉您文件大小的东西都只能告诉您当时的大小。在您访问它之前,文件的大小可能会发生变化。

于 2012-02-04T03:25:58.933 回答
2

1) 从表面上看,您的代码看起来“OK”——我认为它没有任何问题。

2) 否 - 没有任何“C 或 C++ 规范”会影响 fseek。有一个Posix 规范:

3)如果你想要“文件大小”,我的第一选择可能是“stat()”。这是 Posix 规范:

4)如果您的方法“出了问题”,那么我的第一个猜测是“大文件支持”。

例如,许多操作系统具有并行的“fseek()”和“fseek64()”API。

'希望有帮助.. PSM

于 2012-02-03T20:58:57.903 回答
1

POSIX 将 fseek 的返回值定义为“从文件开头以字节为单位”。您at_beg将始终为零(假设这是一个新打开的文件)。

所以,假设:

  1. 该文件是可搜索的
  2. 没有需要关注的并发问题
  3. 文件大小可以用您选择的 fseek/ftell 变体使用的数据类型来表示

那么您的代码应该可以在任何符合 POSIX 的系统上运行。

于 2012-02-03T21:44:36.680 回答