【问题标题】:How do you determine the size of a file in C?你如何确定C中文件的大小?
【发布时间】:2010-09-05 17:06:51
【问题描述】:

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

#include <stdio.h>

unsigned int fsize(char* file){
  //what goes here?
}

【问题讨论】:

  • 您将需要使用库函数来检索文件的详细信息。由于 C 完全独立于平台,因此您需要让我们知道您正在开发的平台/操作系统!
  • 为什么是char* file,为什么不是FILE* file? -1
  • @user12211554 这样...就strlen!
  • 请注意:文件可以在fsizeread 之间增长。小心。

标签: c file io filesize


【解决方案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;
}

【讨论】:

  • 这不是标准 C。它是 POSIX 标准的一部分,但不是 C 标准。
【解决方案2】:

您可以打开文件,使用

转到相对于文件底部的 0 偏移量
#define SEEKBOTTOM   2

fseek(handle, 0, SEEKBOTTOM)  

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

我很久没有用 C 写代码了,但我认为它应该可以工作。

【讨论】:

  • 你不应该定义像 SEEKBOTTOM 这样的东西。 #include fseek(handle, 0, SEEK_END);
【解决方案3】:

Matt 的解决方案应该可以工作,只是它是 C++ 而不是 C,而且不需要最初的 tell。

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)要慢。

【讨论】:

  • 是的,你应该这样做。但是,除非有一个非常令人信服的理由不编写特定于平台的代码,否则您可能应该只使用特定于平台的调用而不是 open/seek-end/tell/close 模式。
  • 很抱歉回复晚了,但我在这里遇到了一个大问题。它会导致应用程序在访问受限文件(如密码保护或系统文件)时挂起。有没有办法在需要时向用户询问密码?
  • @Justin,您可能应该专门针对您遇到的问题提出一个新问题,并提供有关您所在平台的详细信息,您如何访问文件,以及行为是。
  • C99 和 C11 都从 ftell() 返回 long int(unsigned long) 转换不会提高范围,因为它已经受到功能的限制。 ftell() 出错时返回 -1 并且与演员表混淆。建议fsize()返回与ftell()相同的类型。
  • 我同意。演员阵容是为了匹配问题中的原始原型。不过,我不记得为什么我把它变成了 unsigned long 而不是 unsigned int。
【解决方案4】:

**不要这样做(why?):

引用我在网上找到的 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
}

【讨论】:

  • @mezhaka:CERT 报告完全是错误的。 fseekoftello(或者 fseekftell,如果你没有前者并且对可以使用的文件大小的限制感到满意)是确定文件长度的正确方法。基于stat 的解决方案不适用于许多“文件”(例如块设备),并且不能移植到非 POSIX 系统。
  • 这是在许多非 posix 兼容系统(例如我的极简 mbed)上获取文件大小的唯一方法
  • 你绝对不想在这里使用intftell 返回一个有符号的long,它在许多(但不是全部)64 位系统上是一个 64 位类型。在大多数 32 位系统上它仍然只有 32 位,因此您需要 ftellooff_t 才能便携地处理大文件。尽管 ISO C 选择不定义行为,但大多数实现都会这样做,因此这在大多数系统上确实有效。
【解决方案5】:

我找到了a method using fseek and ftell 和一个带有这个问题的线程,其中的答案是不能以其他方式仅在 C 中完成。

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

【讨论】:

    【解决方案6】:

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

    不要使用unsigned int。大小超过 4 GB 的文件很常见,因为有些不太常见

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

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

    【讨论】:

    • 我使用了 off_t 为 32 位的编译器。当然,这是在 4GB 文件不太常见的嵌入式系统上。无论如何,POSIX 还定义了 off64_t 和相应的方法来增加混乱。
    • 我总是喜欢假设 Windows 的答案,除了批评这个问题什么都不做。您能否添加一些符合 POSIX 的内容?
    • @JL2210 Ted Percival 接受的答案显示了一个符合 posix 的解决方案,所以我认为重复显而易见的事情没有任何意义。我(和其他 70 人)认为添加关于 windows 的注释而不使用带符号的 32 位整数来表示文件大小是最重要的增值。干杯
    【解决方案7】:

    如果您正在构建 Windows 应用程序,请使用 GetFileSizeEx API,因为 CRT 文件 I/O 很混乱,尤其是在确定文件长度时,由于不同系统上文件表示的特殊性;)

    【讨论】:

      【解决方案8】:

      在类 Unix 系统上,您可以在已打开的文件描述符(POSIX man page、Linux man page)上使用 POSIX 系统调用:stat on a pathfstat
      (从open(2)fileno(FILE*) 获取stdio 流上的文件描述符)。

      基于 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 定义。
      • 出错时返回-1,而不是0,这对于空文件来说是不明确的。 off_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 位系统上,您应该使用选项 -D_FILE_OFFSET_BITS=64 编译它,否则 off_t 最多只能保存 2 GB 的值。有关详细信息,请参阅Large File Support in Linux 的“使用 LFS”部分。

      【讨论】:

      • 这是 Linux/Unix 特有的——可能值得指出,因为问题没有指定操作系统。
      • 您可以将返回类型更改为 ssize_t 并从 off_t 转换大小而不会遇到任何问题。使用 ssize_t 似乎更有意义 :-) (不要与未签名且不能用于指示错误的 size_t 混淆。)
      • 如需更便携的代码,请使用 Derek 建议的fseek + ftell
      • 如需更便携的代码,请使用 Derek 建议的 fseek + ftell 不,C Standard 明确指出 fseek()SEEK_END on二进制文件是未定义的行为。 7.19.9.2 fseek 函数 ...二进制流不需要有意义地支持 fseek 调用的 wherece 值为 SEEK_END,如下所述,来自第 234 页的脚注。 267 的链接 C 标准,并在二进制流中将 fseekSEEK_END 专门标记为未定义的行为。 .
      • 来自gnu libc manual: ... [非POSIX] 系统区分包含文本的文件和包含二进制数据的文件,ISO C 的输入和输出工具提供了这种区分。 ...在 GNU C 库和所有 POSIX 系统中,文本流和二进制流之间没有区别。当您打开一个流时,无论您是否要求二进制,您都会得到相同类型的流。此流可以处理任何文件内容,并且没有文本流有时具有的限制。
      【解决方案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);
      

      【讨论】:

        【解决方案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 
        }
        

        【讨论】:

        • 不需要关闭文件吗?
        • 不,我不喜欢需要路径的函数。相反,请让 ti 期望一个文件指针
        • 如果您在 Windows 上运行并且文件大小为 14 GB,会发生什么?
        • @AndrewHenle:在这种情况下,您需要使用ftello,它返回一个off_t,即使long 不是,它也可以是64 位类型。我假设ftello 在理论上仍然存在与您描述的in an answer 相同的问题,即寻求二进制流末尾的未定义行为,但是 ISO C 没有提供更好的 AFAIK,所以对于很多程序来说至少- 坏事是依赖实现来定义这种行为。
        • @PeterCordes Windows uses _ftelli64()(什么?!?微软使用了不可移植的功能?在某种程度上导致供应商锁定?!!?说不是这样!)但如果你'依赖于实现定义的行为,您不妨使用实现的方法来获取文件大小。 fileno()stat() 在 Windows 上均受支持,尽管在供应商锁定模式下为 _fileno()_fstat()#ifdef _WIN32 #define fstat _fstat #define fileno _fileno #endif 实际上是最便携的解决方案。
        【解决方案11】:

        试试这个 --

        fseek(fp, 0, SEEK_END);
        unsigned long int file_size = ftell(fp);
        rewind(fp);
        

        首先,寻找文件的末尾;然后,报告文件指针的位置。最后(这是可选的)它倒回到文件的开头。请注意,fp 应该是二进制流。

        file_size 包含文件包含的字节数。请注意,由于(根据 climits.h)unsigned long 类型被限制为 4294967295 字节(4 GB),如果您可能要处理比这更大的文件,则需要找到不同的变量类型。

        【讨论】:

        • 这与 8 年前的 Derek's answer 有什么不同?
        • 对于二进制流,这是未定义的行为,对于文本流ftell 不会返回代表可以从文件中读取的字节数的值。
        【解决方案12】:

        POSIX

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

        概要

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

        示例

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

        #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 不直接提供确定文件长度的方法。
        我们将不得不使用我们的头脑。现在,我们将使用 seek 方法!

        概要

        • 使用fseek(3) 将文件查找到末尾。
        • 使用ftell(3)获取当前位置。

        示例

        #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);
        }
        

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

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

        【讨论】:

        • struct _stat64__stat64() 用于 _Windows。
        • 最后一个例子不正确,fopen 有两个参数
        【解决方案13】:

        我有一个只适用于stdio.h 的函数。我非常喜欢它,而且效果很好而且非常简洁:

        size_t fsize(FILE *File) {
            size_t FSZ;
            fseek(File, 0, 2);
            FSZ = ftell(File);
            rewind(File);
            return FSZ;
        }
        

        【讨论】:

          【解决方案14】:

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

          ULONGLONG GetFileSizeAtt(const wchar_t *wFile)
          {
              WIN32_FILE_ATTRIBUTE_DATA fileInfo;
              ULONGLONG FileSize = 0ULL;
              //https://docs.microsoft.com/nl-nl/windows/win32/api/fileapi/nf-fileapi-getfileattributesexa?redirectedfrom=MSDN
              //https://docs.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;
          }
          

          【讨论】:

            猜你喜欢
            • 2011-01-25
            • 2010-11-10
            • 1970-01-01
            • 1970-01-01
            • 2010-10-30
            • 2010-09-12
            • 1970-01-01
            • 2021-09-14
            • 2010-09-16
            相关资源
            最近更新 更多