你如何确定 C 语言中文件的大小?

How do you determine the size of a file in C?

提问人:andrewrk 提问时间:8/12/2008 最后编辑:hippietrailandrewrk 更新时间:1/7/2023 访问量:155103

问:

如何计算文件的大小(以字节为单位)?

#include <stdio.h>

unsigned int fsize(char* file){
  //what goes here?
}
C 文件 IO 文件大小

评论

0赞 Chris Roberts 8/12/2008
您将需要使用库函数来检索文件的详细信息。由于 C 语言是完全独立于平台的,因此您需要让我们知道您正在为哪个平台/操作系统进行开发!
1赞 9/13/2019
为什么,为什么不呢?-1char* fileFILE* file
0赞 user26742873 2/16/2021
@user12211554这样......只!strlen
1赞 user26742873 2/16/2021
请注意:文件可以在 和 之间增长。小心。fsizeread

答:

4赞 Ecton 8/12/2008 #1

如果您对使用 std c 库感到满意:

#include <sys/stat.h>
off_t fsize(char *file) {
    struct stat filestat;
    if (stat(file, &filestat) == 0) {
        return filestat.st_size;
    }
    return 0;
}

评论

26赞 Derek Park 8/12/2008
这不是标准的 C。它是 POSIX 标准的一部分,但不是 C 标准的一部分。
-3赞 PabloG 8/12/2008 #2

您可以打开文件,从文件底部转到 0 偏移量

#define SEEKBOTTOM   2

fseek(handle, 0, SEEKBOTTOM)  

从 fseek 返回的值是文件的大小。

我已经很久没有用 C 编写代码了,但我认为它应该有效。

评论

16赞 sigjuice 3/26/2009
您不必定义像 SEEKBOTTOM 这样的东西。#include < stdio.h> fseek(句柄, 0, SEEK_END);
0赞 user16217248 1/29/2023
“从 fseek 返回的值是文件的大小”
34赞 Derek Park 8/12/2008 #3

Matt 的解决方案应该有效,只是它是 C++ 而不是 C,并且不需要初始告诉。

unsigned long fsize(char* file)
{
    FILE * f = fopen(file, "r");
    fseek(f, 0, SEEK_END);
    unsigned long len = (unsigned long)ftell(f);
    fclose(f);
    return len;
}

也为你固定了你的支架。;)

更新:这并不是最好的解决方案。它在 Windows 上仅限于 4GB 文件,并且可能比仅使用特定于平台的调用(如 或 )慢。GetFileSizeExstat64

评论

0赞 Derek Park 4/18/2012
是的,你应该。但是,除非有非常令人信服的理由不编写特定于平台的调用,否则您可能应该只使用特定于平台的调用,而不是 open/seek-end/tell/close 模式。
1赞 Justin 3/29/2013
很抱歉回复晚了,但我在这里遇到了一个大问题。它会导致应用程序在访问受限制的文件(如受密码保护的文件或系统文件)时挂起。有没有办法在需要时要求用户输入密码?
0赞 Derek Park 4/2/2013
@Justin,您可能应该专门针对您遇到的问题提出一个新问题,并提供有关您所在的平台、您如何访问文件以及行为的详细信息。
5赞 chux - Reinstate Monica 1/13/2014
C99 和 C11 都从 返回。 投射不会提高范围,因为已经受到功能的限制。 错误时返回 -1,并且与强制转换混淆。建议返回与 相同的类型。long intftell()(unsigned long)ftell()fsize()ftell()
1赞 Peter Cordes 11/25/2021
显然,您不想使用 ,即使在 64 位类型的 64 位系统上,它也无法处理大文件。(例如,大多数非 Windows 64 位系统使用 LP64 ABI)。但实际上,您应该使用 which 返回 ,它在每个支持大文件的系统上都是 64 位。intlongftellooff_t
16赞 9 revs, 6 users 85%andrewrk #4

**不要这样做(为什么?

引用我在网上找到的 C99 标准文档:“将文件位置指示符设置为文件末尾,就像 一样,对于二进制流(因为可能存在尾随空字符)或任何具有状态相关编码的流,这些流肯定不会以初始移位状态结束。fseek(file, 0, SEEK_END)

将定义更改为 int,以便可以传输错误消息,然后使用 和 确定文件大小。fseek()ftell()

int fsize(char* file) {
  int size;
  FILE* fh;

  fh = fopen(file, "rb"); //binary mode
  if(fh != NULL){
    if( fseek(fh, 0, SEEK_END) ){
      fclose(fh);
      return -1;
    }

    size = ftell(fh);
    fclose(fh);
    return size;
  }

  return -1; //error
}

评论

6赞 R.. GitHub STOP HELPING ICE 10/24/2010
@mezhaka:CERT报告是完全错误的。 和(或者,如果你没有前者,并且对你可以使用的文件大小的限制感到满意)是确定文件长度的正确方法。基于的解决方案不适用于许多“文件”(例如块设备),并且不能移植到非 POSIX 系统。fseekoftellofseekftellstat
2赞 Earlz 3/3/2012
这是在许多不符合 posix 的系统(例如我非常简约的 mbed)上获取文件大小的唯一方法
1赞 Peter Cordes 11/25/2021
你绝对不想在这里使用。 返回一个 signed ,它是许多(但不是全部)64 位系统上的 64 位类型。在大多数 32 位系统上,它仍然只有 32 位,因此您需要能够便携地处理大文件。尽管 ISO C 选择不定义行为,但大多数实现都定义了行为,因此这在大多数系统上都适用于实际情况。intftelllongftellooff_t
3赞 Nickolay 8/12/2008 #5

我找到了一种使用 fseek 和 ftell 的方法,以及一个带有这个问题的线程,其答案是它不能以另一种方式在 C 中完成。

您可以使用像 NSPR(支持 Firefox 的库)这样的可移植性库。

82赞 Orion Edwards 8/12/2008 #6

不要使用 .如今,大小超过 2 GB 的文件很常见int

不要使用 .大小超过 4 GB 的文件很常见,因为一些不太常见的污垢unsigned int

IIRC 标准库定义为无符号 64 位整数,这是每个人都应该使用的。我们可以在几年内将其重新定义为 128 位,当我们开始有 16 EB 文件挂在身边时。off_t

如果你使用的是 Windows,你应该使用 GetFileSizeEx - 它实际上使用一个有符号的 64 位整数,所以他们会开始遇到 8 EB 文件的问题。愚蠢的Microsoft!:-)

评论

3赞 Aaron Campbell 7/8/2016
我使用过 off_t 为 32 位的编译器。当然,这是在嵌入式系统上,4GB文件不太常见。无论如何,POSIX还定义了off64_t和相应的方法来增加混乱。
1赞 S.S. Anne 4/28/2019
我总是喜欢假设 Windows 的答案,除了批评问题之外什么都不做。您能否添加一些符合 POSIX 标准的内容?
2赞 Orion Edwards 4/28/2019
@JL2210 Ted Percival 接受的答案显示了一个符合 posix 的解决方案,所以我认为重复显而易见的事情毫无意义。我(和其他 70 人)认为添加有关 Windows 的注释并且不使用带符号的 32 位整数来表示文件大小是在此基础上的附加值。干杯
4赞 user2189331 8/12/2008 #7

如果要生成 Windows 应用,请使用 GetFileSizeEx API,因为 CRT 文件 I/O 很混乱,尤其是在确定文件长度时,这是由于不同系统上的文件表示形式的特殊性;)

176赞 T Percival 8/12/2008 #8

在类 Unix 系统上,您可以在路径上或已打开的文件描述符(POSIX 手册页、Linux 手册页)上使用 POSIX 系统调用:stat
(从 或 stdio 流中获取文件描述符)。
fstatopen(2)fileno(FILE*)

基于 NilObject 的代码:

#include <sys/stat.h>
#include <sys/types.h>

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

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

    return -1; 
}

变化:

  • 将文件名参数设为 .const char
  • 更正了缺少变量名称的定义。struct stat
  • Error 时返回,而不是 ,这对于空文件来说是不明确的。 是有符号类型,因此这是可能的。-10off_t

如果要在出错时打印消息,可以使用以下命令:fsize()

#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

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

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

    fprintf(stderr, "Cannot determine size of %s: %s\n",
            filename, strerror(errno));

    return -1;
}

在 32 位系统上,您应该使用选项进行编译,否则最多只能保存 2 GB 的值。有关详细信息,请参阅 Linux 中的大文件支持的“使用 LFS”部分。-D_FILE_OFFSET_BITS=64off_t

评论

22赞 Drew Hall 8/3/2010
这是特定于 Linux/Unix 的——可能值得指出,因为问题没有指定操作系统。
1赞 T Percival 8/7/2010
您可以将返回类型更改为 ssize_t,然后毫不费力地从off_t中转换大小。使用ssize_t似乎更有意义:-)(不要与未签名且不能用于指示错误的size_t混淆。
1赞 Ciro Santilli OurBigBook.com 3/2/2015
对于更可移植的代码,请使用 Derek 建议的 +。fseekftell
14赞 Andrew Henle 4/6/2016
对于更可移植的代码,请使用 Derek 建议的 fseek + ftell不。C 标准明确指出,在二进制文件上是未定义的行为。7.19.9.2 fseek 函数 ...二进制流不需要有意义地支持值为 SEEK_ENDfseek 调用,如下所述,该调用来自链接的 C 标准第 267 页的脚注 234,并且专门将二进制流中的 to 标记为未定义的行为。.fseek()SEEK_ENDfseekSEEK_END
1赞 Small Boy 8/14/2020
来自 gnu libc 手册: ...[非 POSIX] 系统对包含文本的文件和包含二进制数据的文件进行了区分,ISO C 的输入和输出工具提供了这种区分。...在 GNU C 库和所有 POSIX 系统上,文本流和二进制流之间没有区别。当您打开流时,无论您是否要求二进制文件,您都会获得相同类型的流。此流可以处理任何文件内容,并且没有文本流有时具有的限制。
3赞 rco16 2/8/2014 #9

我使用这组代码来查找文件长度。

//opens a file with a file descriptor
FILE * i_file;
i_file = fopen(source, "r");

//gets a long from the file descriptor for fstat
long f_d = fileno(i_file);
struct stat buffer;
fstat(f_d, &buffer);

//stores file size
long file_length = buffer.st_size;
fclose(i_file);

评论

0赞 Andreas Wenzel 7/5/2023
此解决方案使用特定于平台的功能。它可能不适用于非 POSIX 平台。如果你为一个与平台无关的问题提供了一个特定于平台的答案,那么我建议你清楚地标记它。
-2赞 Abdessamad Doughri 6/6/2016 #10

下面是一个简单而干净的函数,用于返回文件大小。

long get_file_size(char *path)
{
    FILE *fp;
    long size = -1;
    /* Open file for reading */
    fp = fopen(path, "r");
    fseek(fp, 0, SEEK_END);
    size = ftell(fp); 
    fclose(fp);
    return size;
}

评论

0赞 10/13/2019
不,我不喜欢需要路径的函数。相反,请使 ti exppect 成为文件指针
0赞 M.M 2/17/2021
ftell对于文本文件,可能不是字节偏移量(在文本模式下打开文件)
1赞 Andrew Henle 3/4/2021
如果您在 Windows 上运行并且文件大小为 14 GB,会发生什么情况?
1赞 Peter Cordes 11/25/2021
@AndrewHenle:在这种情况下,您需要使用 which 返回 ,它可以是 64 位类型,即使不是。我认为理论上仍然存在同样的问题,即正如您在答案中描述的那样,在寻求二进制流末尾的未定义行为,但 ISO C 没有提供更好的 AFAIK,因此对于许多程序来说,最不坏的事情是依靠实现来定义这种行为。ftellooff_tlongftello
3赞 Andrew Henle 11/25/2021
@PeterCordes Windows 使用 _ftelli64() (什么?!?Microsoft 使用不可移植功能?在某种程度上导致供应商锁定?!!?说不是这样!但是,如果您依赖于实现定义的行为,则不妨使用实现的方法来获取文件大小。两者在 Windows 上都受支持,尽管在供应商锁定模式下为 和 。 实际上是最便携的解决方案。fileno()stat()_fileno()_fstat()#ifdef _WIN32 #define fstat _fstat #define fileno _fileno #endif
12赞 user9258013 1/10/2019 #11

POSIX的

POSIX 标准有自己的方法来获取文件大小。
包含标头以使用该函数。
sys/stat.h

概要

  • 使用 stat(3) 获取文件统计信息。
  • 获取属性。st_size

例子

注意:它将大小限制为 。如果不是文件系统,请使用 64 位版本!4GBFat32

#include <stdio.h>
#include <sys/stat.h>

int main(int argc, char** argv)
{
    struct stat info;
    stat(argv[1], &info);

    // 'st' is an acronym of 'stat'
    printf("%s: size=%ld\n", argv[1], info.st_size);
}
#include <stdio.h>
#include <sys/stat.h>

int main(int argc, char** argv)
{
    struct stat64 info;
    stat64(argv[1], &info);

    // 'st' is an acronym of 'stat'
    printf("%s: size=%ld\n", argv[1], info.st_size);
}

ANSI C(标准)

ANSI C 不直接提供确定文件长度的方法。
我们必须动脑筋。现在,我们将使用搜索方法!

概要

#include <stdio.h>

int main(int argc, char** argv)
{
    FILE* fp = fopen(argv[1]);
    int f_size;

    fseek(fp, 0, SEEK_END);
    f_size = ftell(fp);
    rewind(fp); // to back to start again

    printf("%s: size=%ld", (unsigned long)f_size);
}

如果文件是 或 管道。POSIX、ANSI C 不起作用。
如果文件是管道或 ,它将返回。
stdin0stdin

意见: 您应该改用 POSIX 标准。因为,它有 64 位支持。

评论

2赞 Bob Stein 4/17/2019
struct _stat64和_Windows。__stat64()
1赞 M.M 2/17/2021
最后一个例子是不正确的,需要两个参数fopen
2赞 Andreas Wenzel 4/23/2022
在 ISO C 中,当文件以二进制模式打开时,函数 ftell 只能保证从文件开头开始的字节数。但是,在文本模式下,返回的值未指定,并且仅对 有意义。ftellfseek
-2赞 user12211554 7/12/2019 #12

我有一个功能,只能很好地工作.我非常喜欢它,它运行良好且非常简洁:stdio.h

size_t fsize(FILE *File) {
    size_t FSZ;
    fseek(File, 0, 2);
    FSZ = ftell(File);
    rewind(File);
    return FSZ;
}
0赞 BigChief 1/10/2022 #13

从Windows文件详细信息中提取的C++ MFC,不确定这是否比搜索性能更好,但如果它是从元数据中提取的,我认为它更快,因为它不需要读取整个文件

ULONGLONG GetFileSizeAtt(const wchar_t *wFile)
{
    WIN32_FILE_ATTRIBUTE_DATA fileInfo;
    ULONGLONG FileSize = 0ULL;
    //https://learn.microsoft.com/nl-nl/windows/win32/api/fileapi/nf-fileapi-getfileattributesexa?redirectedfrom=MSDN
    //https://learn.microsoft.com/nl-nl/windows/win32/api/fileapi/ns-fileapi-win32_file_attribute_data?redirectedfrom=MSDN
    if (GetFileAttributesEx(wFile, GetFileExInfoStandard, &fileInfo))
    {
        ULARGE_INTEGER ul;
        ul.HighPart = fileInfo.nFileSizeHigh;
        ul.LowPart = fileInfo.nFileSizeLow;
        FileSize = ul.QuadPart;
    }
    return FileSize;
}
1赞 Andreas Wenzel 1/7/2023 #14

在普通的 ISO C 中,只有一种方法可以确定文件的大小,并保证有效:从头开始读取整个文件,直到遇到文件末尾。

然而,这是非常低效的。如果您想要一个更有效的解决方案,那么您将不得不

  • 依赖于特定于平台的行为,或者
  • 还原为特定于平台的函数,例如 Linux 上的 stat 或 Microsoft Windows 上的 GetFileSize

与其他答案所建议的相反,以下代码不能保证有效:

fseek( fp, 0, SEEK_END );
long size = ftell( fp );

即使我们假设数据类型足够大来表示文件大小(这在某些平台上是有问题的,尤其是 Microsoft Windows),发布的代码也存在以下问题:long

发布的代码不能保证在文本流上工作,因为根据 ISO C11 标准的 §7.21.9.4 ¶2,返回的文件位置指示器的值包含未指定的信息。只有对于二进制流,此值才能保证为从文件开头开始的字符数。文本流没有这样的保证。ftell

发布的代码也不能保证在二进制流上工作,因为根据 ISO C11 标准的 §7.21.9.2 ¶3,二进制流不需要有意义地支持 .SEEK_END

话虽如此,在大多数常见的平台上,如果我们假设数据类型足够大以表示文件大小,则发布的代码将起作用。long

但是,在 Microsoft Windows 上,字符(回车符后跟换行符)将转换为文本流(但不用于二进制流),因此您获得的文件大小将计为两个字节,尽管您在文本模式下只读取单个字符 ()。因此,您获得的结果将不一致。\r\n\n\r\n\n

在基于 POSIX 的平台(例如 Linux)上,这不是问题,因为在这些平台上,文本模式和二进制模式之间没有区别。

评论

1赞 Andrew Henle 1/7/2023
另一个 Windows 问题:在 Windows 上只有 4 个字节,这意味着在 Windows 上大于 2 GB 的文件将失败。longftell()
0赞 Andreas Wenzel 1/7/2023
@AndrewHenle:是的,这是很重要的一点。同时,我已经编辑了我的答案。我相信我现在已经在我的回答中谈到了你的观点。