在C语言中获取大文件的大小

14
在没有重复问题的情况下,我已经彻底检查了SO,但似乎还没有清晰的答案,尽管问题看起来很简单。我正在寻找一种便携式C代码,即使这样的文件大于4GB,也能提供文件的大小。通常的方法(fseek,ftell)可以正常工作,只要文件保持小于2GB。它已经得到了很好的支持,因此我正在尝试找到等效的东西。不幸的是,更新的方法(fseeko,ftello)并非所有编译器都支持。例如,MinGW错过了它(显然MSVC也是如此)。此外,一些注释让我相信新的返回类型(off_t)不一定支持大小> 2GB,这可能取决于一些外部参数,需要进行检查。不明确的方法(fseeko64,ftello64)不受MSVC支持。 MS提供它们的等效物,_fseeki64和_ftelli64。这已经很糟糕了,但情况变得更糟:在运行时,一些Linux配置似乎不支持这些功能。例如,我的Debian Squeeze on PowerPC使用GCC 4.4将产生一个使用fseeko64的“filesize”方法,该方法始终返回0(而在Ubuntu64上却很好地工作)。 MinGW似乎会在2GB以上回答一些随机垃圾。总之,就可移植性而言,我有点茫然无措。如果我需要制作一堆#if #else,那么为什么不直接去操作系统和编译器特定的方法呢,例如对于MSVC的GetFileSize()方法。

3
“便携”对你来说是什么意思呢?有很多系统甚至无法打开文件,更有甚者无法打开超过4GB大小的文件。 - Johan Kotlinski
6个回答

9

你说得对,没有通用的方法;如果我是你,我会在Windows上使用GetFileSize,POSIX上使用stat


2
你可以在Windows上使用_stat64来保持代码大致相同。 - user7116
1
@sixlettervariables:正确,尽管我不知道Windows上的每个编译器是否都实现了它(虽然GetFileSize是Windows API的一部分,因此应始终可用)。 - Matteo Italia

9
你应该能够使用 stat64 在Linux上_stat64 在Windows上 来获取超过 2GB 的文件大小信息,这两个函数的用法非常相似。你也可以使用一些 #define 来在 Windows 上使用 stat64
#if __WIN32__
#define stat64 _stat64
#endif

然而,需要注意的是,在Windows操作系统中,_stat函数族实际上只是其他函数的包装器,因此会增加额外的资源和时间开销。

6
int ch;
FILE *f = fopen("file_to_analyse", "rb");
/* error checking ommited for brevity */
unsigned long long filesize = 0; /* or unsigned long for C89 compatability*/
while ((ch = fgetc(f)) != EOF) filesize++;
fclose(f);
/* error checking ommited for brevity */

3
好的,这是唯一符合标准的方法,但我希望你是在讽刺:逐个字符地阅读整个文件,可能达到2GB以上,仅仅为了知道它的大小(在当前文件系统上,这只是文件的属性)是很愚蠢的... - Matteo Italia
2
哦,不,不,不……请告诉我你在开玩笑。另一方面,这个问题是关于可移植性的,而不是效率。这确实是一种可移植的方式。 - Daniel Kamil Kozar
2
它是事件驱动的,这就是它如此快速的原因。 - Matt Joiner
为什么这样不好?要不然你怎么数所有的字节呢?你必须迭代它们并实际计算才能找出答案,对吧? - Gerard
3
@Gerard 因为文件系统在写入字节时计算它们的数量,然后将该值存储起来。这就是它知道EOF的方式。读取整个大文件以确定大小很慢,而读取存储在文件系统中的预先计算好的字段则很快。 - Unsigned
@Gerard 你需要读完一本书的每一页才能知道它有多少页吗?如果你这样做了,你会问为什么这样做很糟糕吗? - Jim Balter

3

我已经实现并测试了以下内容:

#if __WIN32__
#define stat64 _stat64
#endif

使用MinGW64 gcc编译器4.8.1和Linux gcc 4.6.3编译并工作。在OSX上,不需要重新定义stat。对于lstat和fstat函数,我期望类似的宏#define可用。

1
#include sys/stat.h

off_t fsize(const char *filename) {
    struct stat st; 

    if (stat(filename, &st) == 0)
        return st.st_size;

    return -1; 
}

1

使用 lseek()(或 _lseek())和 SEEK_END 怎么样?它会返回所寻找的偏移量。

在 Linux 下,需要将 _FILE_OFFSET_BITS 定义为 64,才能使 lseek() 返回 64 位值(这应该是默认值)。


我还没有尝试过。看起来lseek()可能会有与fseeko()相同的问题:使用的类型(off_t)可能或可能不支持超过2GB的值,这取决于某些外部配置。 - Cyan
@Attract:我在32/64位的Linux下使用gcc,在32位Win-Vista下使用VC10进行了测试。 - alk

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