如何在 C 中获取文件的大小? [复制]

Posted

技术标签:

【中文标题】如何在 C 中获取文件的大小? [复制]【英文标题】:How can I get a file's size in C? [duplicate] 【发布时间】:2010-09-19 07:28:03 【问题描述】:

如何找出我用 C 语言编写的应用程序打开的文件的大小? 我想知道大小,因为我想将加载文件的内容放入一个字符串中,我使用malloc() 分配它。只写malloc(10000*sizeof(char)); 恕我直言是个坏主意。

【问题讨论】:

请注意 sizeof(char) 根据定义为 1。 是的,但某些深奥平台的编译器可能会将 char 定义为 2 个字节 - 然后程序分配的内存超出了必要的范围。永远不能太确定。 @George 一个“深奥平台的编译器”,其中 sizeof(char) != 1 不是真正的 C 编译器。即使一个字符是 32 位,它仍然会返回 1。 @George:C(和 C++)标准保证 sizeof(char)==1。参见例如parashift.com/c++-faq-lite/intrinsic-types.html#faq-26.1 在分配 x 个字符时,我实际上更喜欢 malloc(x*sizeof(char)); 而不是 malloc(x);。是的,它们总是编译成相同的东西,但我喜欢与其他内存分配保持一致。 【参考方案1】:

你需要寻找到文件末尾,然后询问位置:

fseek(fp, 0L, SEEK_END);
sz = ftell(fp);

然后你可以回溯,例如:

fseek(fp, 0L, SEEK_SET);

或(如果想从头开始)

rewind(fp);

【讨论】:

@camh - 谢谢。此评论解决了我在使用文件大小算法时遇到的问题。作为记录,通过在 fopen 的模式字符串末尾添加一个“b”以二进制模式打开文件。 你,在人们忘记它的含义之前使用rewind 返回一个有符号整数,因此限制为 2 GB。但从好的方面来说,您的文件长度可能为负 20 亿字节,他们为此做好了准备。 length = lseek(fd, 0, SEEK_END)+1; 来自fseek documentation “允许库实现不支持 SEEK_END(因此,使用它的代码没有真正的标准可移植性)。”【参考方案2】:

使用标准库:

假设您的实现有意义地支持 SEEK_END:

fseek(f, 0, SEEK_END); // seek to end of file
size = ftell(f); // get current file pointer
fseek(f, 0, SEEK_SET); // seek back to beginning of file
// proceed with allocating memory and reading the file

Linux/POSIX:

您可以使用stat(如果您知道文件名)或fstat(如果您有文件描述符)。

以下是 stat 的示例:

#include <sys/stat.h>
struct stat st;
stat(filename, &st);
size = st.st_size;

Win32:

您可以使用GetFileSize 或GetFileSizeEx。

【讨论】:

请注意,为了清楚起见,我省略了错误检查。 您不需要文件名 - 您可以使用 fstat。 你需要点stat struct的地址。第二行应该是:stat(filename, &st); 为了 -FATAL ERROR, EXITING,我省略了错误检查。 第二个选项是唯一可以显示大于 2GB 的文件的选项【参考方案3】:

如果您有文件描述符fstat(),则返回一个包含文件大小的统计结构。

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

// fd = fileno(f); //if you have a stream (e.g. from fopen), not a file descriptor.
struct stat buf;
fstat(fd, &buf);
off_t size = buf.st_size;

【讨论】:

添加“fd = fileno(f);”如果您有一个流(例如来自 fopen),而不是文件描述符。需要错误检查。 当然需要进行错误检查——这只会使示例复杂化。 在我看来,这是最好的真正答案,而且我认为我们在 C 语言中的大部分时间都处于关闭状态,在我们的示例中我们真的需要错误检查和其他不必要的代码吗? M$DN 的做法已经够糟糕了,我们不要效仿,而是在最后说“确保添加错误检查”并完成它。 很多 SO 的用户是 C 的学生,而不是过去的大师。因此,答案中给出的代码应该显示错误检查,以便学生学习正确的编码方式。 详细说明 (f)stat() 返回块分配总字节数,而 fseek() / ftell() 序列返回遇到 EOF 之前的字节数。【参考方案4】:

我最终只是做了一个简短而甜蜜的fsize 函数(注意,没有错误检查)

int fsize(FILE *fp)
    int prev=ftell(fp);
    fseek(fp, 0L, SEEK_END);
    int sz=ftell(fp);
    fseek(fp,prev,SEEK_SET); //go back to where we were
    return sz;

标准 C 库没有这样的功能有点傻,但我明白为什么它会很困难,因为不是每个“文件”都有大小(例如 /dev/null

【讨论】:

恢复文件流先前位置指示器的好点。 ftell(fp) 返回long。无需可能短路到int 并丢失信息。【参考方案5】:

如何使用lseek/fseek/stat/fstat 获取文件大小?

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

void
fseek_filesize(const char *filename)

    FILE *fp = NULL;
    long off;

    fp = fopen(filename, "r");
    if (fp == NULL)
    
        printf("failed to fopen %s\n", filename);
        exit(EXIT_FAILURE);
    

    if (fseek(fp, 0, SEEK_END) == -1)
    
        printf("failed to fseek %s\n", filename);
        exit(EXIT_FAILURE);
    

    off = ftell(fp);
    if (off == -1)
    
        printf("failed to ftell %s\n", filename);
        exit(EXIT_FAILURE);
    

    printf("[*] fseek_filesize - file: %s, size: %ld\n", filename, off);

    if (fclose(fp) != 0)
    
        printf("failed to fclose %s\n", filename);
        exit(EXIT_FAILURE);
    


void
fstat_filesize(const char *filename)

    int fd;
    struct stat statbuf;

    fd = open(filename, O_RDONLY, S_IRUSR | S_IRGRP);
    if (fd == -1)
    
        printf("failed to open %s\n", filename);
        exit(EXIT_FAILURE);
    

    if (fstat(fd, &statbuf) == -1)
    
        printf("failed to fstat %s\n", filename);
        exit(EXIT_FAILURE);
    

    printf("[*] fstat_filesize - file: %s, size: %lld\n", filename, statbuf.st_size);

    if (close(fd) == -1)
    
        printf("failed to fclose %s\n", filename);
        exit(EXIT_FAILURE);
    


void
stat_filesize(const char *filename)

    struct stat statbuf;

    if (stat(filename, &statbuf) == -1)
    
        printf("failed to stat %s\n", filename);
        exit(EXIT_FAILURE);
    

    printf("[*] stat_filesize - file: %s, size: %lld\n", filename, statbuf.st_size);



void
seek_filesize(const char *filename)

    int fd;
    off_t off;

    if (filename == NULL)
    
        printf("invalid filename\n");
        exit(EXIT_FAILURE);
    

    fd = open(filename, O_RDONLY, S_IRUSR | S_IRGRP);
    if (fd == -1)
    
        printf("failed to open %s\n", filename);
        exit(EXIT_FAILURE);
    

    off = lseek(fd, 0, SEEK_END);
    if (off == -1)
    
        printf("failed to lseek %s\n", filename);
        exit(EXIT_FAILURE);
    

    printf("[*] seek_filesize - file: %s, size: %lld\n", filename, (long long) off);

    if (close(fd) == -1)
    
        printf("failed to close %s\n", filename);
        exit(EXIT_FAILURE);
    


int
main(int argc, const char *argv[])

    int i;

    if (argc < 2)
    
        printf("%s <file1> <file2>...\n", argv[0]);
        exit(0);
    

    for(i = 1; i < argc; i++)
    
        seek_filesize(argv[i]);
        stat_filesize(argv[i]);
        fstat_filesize(argv[i]);
        fseek_filesize(argv[i]);
    

    return 0;

【讨论】:

if(off == (-1L))不需要(long) 不幸的是,ftell 返回一个long。您需要ftello 才能返回off_t。 (或者显然在 Windows 上,_ftelli64(),因为他们似乎喜欢让编写可移植代码变得更加困难。)参见 discussion on another answer fstat 仅在您已经打开文件或作为打开文件过程的一部分时才有意义。你的fstat_filesize 不是你想要以那种形式使用的东西,只有当你真的要保留fd 并从中读取它或其他东西时。 open/fstat/close 比stat 优势为零;我会编写该函数来获取FILE *fp(使用fileno())或int fd。我猜你的函数不打算按原样使用,因为它们只打印结果而不是返回结果。 另外,由于您没有将O_CREAT 传递给open,因此未使用第三个参数。 S_IRUSR | S_IRGRP 在那里没有意义。如果open 将创建文件,这将给它0440 aka r--r----- 权限(这将阻止其他任何东西打开和写入它),但它不会没有O_CREAT 所以原型的int open(const char *pathname, int flags); 形式适用。 man7.org/linux/man-pages/man2/open.2.html 除了fstat_filesize 的设计之外,是的,这是一个关于如何进行错误检查的有用示例。除非您应该 fprintf(stderr, ... 提供您的错误消息。在使用 POSIX stat 和朋友的函数中,您应该使用 strerror 作为其中的一部分来获取失败的实际原因,例如 ENOENT 的“没有这样的文件或目录”或“权限被拒绝” EPERM。这更有用,也是报告 Unix 程序错误的标准方法。 (系统调用和文件名总比没有好,如果你不告诉他们,用户可能不会考虑权限。)【参考方案6】:

您是否考虑过不计算文件大小而只在必要时增加数组?这是一个示例(省略了错误检查):

#define CHUNK 1024

/* Read the contents of a file into a buffer.  Return the size of the file 
 * and set buf to point to a buffer allocated with malloc that contains  
 * the file contents.
 */
int read_file(FILE *fp, char **buf) 

  int n, np;
  char *b, *b2;

  n = CHUNK;
  np = n;
  b = malloc(sizeof(char)*n);
  while ((r = fread(b, sizeof(char), CHUNK, fp)) > 0) 
    n += r;
    if (np - n < CHUNK)  
      np *= 2;                      // buffer is too small, the next read could overflow!
      b2 = malloc(np*sizeof(char));
      memcpy(b2, b, n * sizeof(char));
      free(b);
      b = b2;
    
  
  *buf = b;
  return n;

这具有即使对于无法获取文件大小的流(如标准输入)也有效的优势。

【讨论】:

也许这里可以使用realloc 函数,而不是使用中间指针并且必须使用free() 这有一个非常实际的缺点,即 O(n^2) ......你必须复制的东西的大小会增长。小文件还可以,大文件太可怕了。如果你有一个 1k 的块和一个 100M 的文件,你最终会复制(如果我的数学正确的话)大约 1E17 个字节。这可能是一个病态的例子,但它说明了为什么你不应该这样做。 除非我误读,否则每次存储的大小都会翻倍。因此运行时间是 O(n) 而不是 O(n^2)。这与通常用于 std::vector 及其同类的分配策略相同。无论如何,重新分配的效率仍然低于查询文件大小并一次读取所有内容。 这个在每次重新分配时加倍。任何大于 1 的常数因子调整都足以获得 O(n) 界限,字面加倍可能是矫枉过正,按 1.75 缩放例如使用np += (np / 2) + (np / 4); - 所有整数,中间结果不会“提前”溢出。我更可能使用 1.5,但 1.75 更好地展示了这个想法。当然要注意溢出,特别是当实际大小没有溢出时,以前大小的任何倍数都可能溢出。如果您的文件大小为(2^31)-1,这可能会尝试使用-(2^31) 而不是2^31 字节分配缓冲区。 我可能应该警告np += (np / 2) + (np / 4) 没有给出精确乘以 1.75 - 结果可能太小,因为没有进位从被截断的位传播 - 但它应该足够好目的。对于乘以 1.5,np += (np / 2); 应该是正确的。【参考方案7】:

如果您使用的是 Linux,请认真考虑只使用 glib 中的 g_file_get_contents 函数。它处理加载文件、分配内存和处理错误的所有代码。

【讨论】:

如果你在 Linux 并且想要依赖 glib,那就是。 问题不大,因为现在 GTK 和 KDE 应用程序都在使用 glib。它也可以在 Mac OS X 和 Windows 上使用,但在那里并不像标准配置。 但是 glib 不是 c++ 库吗?问题规定C @DaveAppleton:不,glib 是一个非常简单的 C 库,而不是 C++。 @BenCombee glib's not on android, last I check.【参考方案8】:
#include <stdio.h>

#define MAXNUMBER 1024

int main()

    int i;
    char a[MAXNUMBER];

    FILE *fp = popen("du -b  /bin/bash", "r");

    while((a[i++] = getc(fp))!= 9)
        ;

    a[i] ='\0';

    printf(" a is %s\n", a);

    pclose(fp);
    return 0;
  

HTH

【讨论】:

此解决方案过于复杂且效率低下。无需执行命令并解析其输出,正如上面的答案所表明的那样。 此外,这是一个仅限 linux 的解决方案

以上是关于如何在 C 中获取文件的大小? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 中获取文件大小? [复制]

如何在 C 中获取目录的大小?

如何在没有 fseek 和 ftell 的情况下在 ANSI C 中获取文件大小?

在 PHP 中获取大文件(> 2 GB)文件大小的最佳方法? [复制]

如何获取目录C ++的磁盘大小

如何使用C#(Visual Studio)从TFS(Team Foundation Server)获取/复制文件