使用fseek()计算文件大小的方法是否被推荐?

5
在C语言中,我们可以使用fseek()函数来查找文件的大小。例如:
if (fseek(fp, 0L, SEEK_END) != 0)
{
    //  Handle repositioning error
}

所以,我有一个问题,使用 fseek()ftell() 计算文件大小是推荐的方法吗?


相关链接:https://dev59.com/oHVD5IYBdhLWcg3wWKPc(请阅读各种答案)。 - user707650
3
根据C标准[ISO/IEC 9899:2011]的一句话:“将文件位置指示器设置为文件结尾,例如使用fseek(file, 0, SEEK_END),对于二进制流(因为可能存在尾随的空字符)或任何具有状态依赖编码且不确定以初始转换状态结束的流都具有未定义行为。”因此,简单的答案是否定的。 - user707650
2
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - AnT stands with Russia
1
对于文本文件,ftell 的返回值不一定是字节偏移量,因此它不是确定文本文件大小的可移植方式。它只是指定您可以将其传回 fseek(使用 SEEK_SET)以返回流中的那个位置。 - Adrian McCarthy
抱歉,您需要调用 fseek(2) 两次,因为 fseek(2) 系统调用会告诉您指针在移动之前的位置。 - Luis Colorado
4个回答

7
如果您使用的是Linux或其他类UNIX系统,则需要使用stat函数:
struct stat statbuf;
int rval;

rval = stat(path_to_file, &statbuf);
if (rval == -1) {
    perror("stat failed");
} else {
    printf("file size = %lld\n", (long long)statbuf.st_size;
}

在MSVC下的Windows系统中,您可以使用_stati64
struct _stati64 statbuf;
int rval;

rval = _stati64(path_to_file, &statbuf);
if (rval == -1) {
    perror("_stati64 failed");
} else {
    printf("file size = %lld\n", (long long)statbuf.st_size;
}

与使用fseek不同,这种方法不涉及打开文件或在文件中寻找。它只读取文件元数据。

如果数据已写入流但尚未刷新到文件中,则“stat”返回的文件大小将不包括它。 - chqrlie

5

fseek()/ftell()有时候工作。

if (fseek(fp, 0L, SEEK_END) != 0) 
  printf("Size: %ld\n", ftell(fp));
}

问题。
  1. 如果文件大小超过LONG_MAXlong int ftell(FILE *stream)的响应会出现问题。

  2. 如果以文本模式打开文件,则从ftell()返回的值可能不对应于文件长度。 "对于文本流,其文件位置指示器包含未指定的信息",C11dr §7.21.9.4 2

  3. 如果以二进制模式打开文件,fseek(fp, 0L, SEEK_END)没有很好的定义。 "将文件位置指示器设置为文件末尾,如使用fseek(file, 0, SEEK_END),对于具有状态相关编码且不确保以初始转换状态结束的任何流(由于可能存在尾随空字符)或二进制流,其行为是未定义的",C11dr脚注268。 @Evert这通常适用于早期平台,但仍是规范的一部分。

  4. 如果文件是类似串行输入或stdin的流,则fseek(file, 0, SEEK_END)没有太多意义。

通常找到文件大小的解决方案是非便携式的,与特定平台相关。一个好的例子是 @dbush
注意:如果代码试图根据文件大小分配内存,则可用内存很容易被文件大小超过。
由于这些问题,我不建议使用此方法。
通常应重新设计问题,不需要查找文件大小,而是随着更多输入的处理而增加数据。

LL 免责声明:请注意,C语言规范的脚注是说明性质,因此不一定是规范性质


抱歉,您需要调用 fseek(2) 两次,因为 fseek(2) 系统调用会告诉您指针在移动之前的位置。 - Luis Colorado
@LuisColorado:与lseek不同,fseek不会返回文件位置。 - chqrlie
哎呀...你说得对。抱歉。但请记住,它们是库函数,最终会导致系统调用。所以ftell最终会导致两个系统调用fseek(2),这是最有可能的情况。 - Luis Colorado
抱歉,之前我写的是 fseek(2),实际应该是 lseek(2)(系统调用)。这是我的笔误。 - Luis Colorado

0

3
C语言和C标准库都不知道fstat函数。 - AnT stands with Russia

0

你可以用几种方法估算文件的大小:

  • 你可以从头到尾read(2)读取文件,读取的字符数就是文件的大小。这是一种繁琐的获取文件大小的方式,因为你必须读取整个文件才能得到大小。但如果操作系统不允许任意定位文件指针,则这是获取文件大小的唯一方法。
  • 或者你可以将指针移动到文件末尾位置。这就是你在问题中展示的lseek(2),但要小心,你必须调用系统调用两次,因为返回的值是移动指针到所需位置之前的实际位置。
  • 或者你可以使用stat(2)系统调用,它会告诉你文件的所有管理信息,如所有者、组、权限、大小、文件在磁盘上占用的块数、该文件所属的磁盘、指向它的目录条目数等。这使你可以通过仅一次系统调用获取所有这些信息。

你提到的其他方法(例如使用 ftell(3) 标准库调用)也可以工作(但会导致两个系统调用来设置和检索/恢复文件指针),但涉及到可能没有用于其他任何事情的库的问题。获取一个 FILE * 指针(例如使用 fdopen(3))以便能够在其上使用 ftell(3) 函数(两次),然后再使用 fclose(3) 关闭它应该是很复杂的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接