Как я могу получить размер файла на C?

Как я могу узнать размер файла, который я открыл с помощью приложения, написанного на C? Я хотел бы знать размер, потому что я хочу поместить содержимое загруженного файла в строку, которую я выделяю с помощью malloc(). Просто писать malloc(10000*sizeof(char)); - ИМХО плохая идея.


person Nino    schedule 26.10.2008    source источник
comment
Обратите внимание, что sizeof (char) по определению равен 1.   -  person Randy Proctor    schedule 29.10.2009
comment
Да, но компилятор какой-нибудь эзотерической платформы может определять char как 2 байта - тогда программа выделяет больше, чем необходимо. Никогда нельзя быть слишком уверенным.   -  person Nathan Osman    schedule 05.01.2010
comment
@George компилятор эзотерической платформы, где sizeof (char)! = 1 не является настоящим компилятором C. Даже если символ 32-битный, он все равно вернет 1.   -  person Andrew Flanagan    schedule 06.12.2010
comment
@George: Стандарт C (и C ++) гарантирует, что sizeof(char)==1. См., Например, parashift.com/c++-faq-lite/ intrinsic-types.html # faq-26.1   -  person sleske    schedule 08.02.2011
comment
На самом деле я предпочитаю malloc(x*sizeof(char)); malloc(x); при выделении x символов. Да, они всегда компилируются в одно и то же, но мне нравится согласованность с другими выделениями памяти.   -  person moltenform    schedule 16.04.2011
comment
Я надеюсь, что оптимизатор поймет это и сделает правильные вещи, поэтому использование sizeof безопаснее и эквивалентно   -  person Ben    schedule 12.07.2012
comment
@Ben: писать больше, чем нужно, не безопаснее, это может быть опаснее. Больше кода представляет собой большую поверхность для заражения ошибками. Если вы действительно хотите большей безопасности, используйте p = malloc(N * sizeof (*p)) - не кодируйте жестко тип, при котором компилятор не может проверить это за вас.   -  person Bernd Jendrissek    schedule 19.01.2014
comment
Вы можете использовать fstat с fileno, если у вас есть FILE*: fstat(fileno(f), &stat)   -  person sshilovsky    schedule 16.02.2014
comment
Стоит помнить, что стандарт C переопределяет слово byte для обозначения char, поэтому лучше вообще избегать разговоров о байтах в контексте C. (Вместо этого попробуйте октеты. Насколько мне известно, стандарт их не изменил.)   -  person David Given    schedule 20.05.2017


Ответы (8)


Вам нужно перейти к концу файла, а затем запросить позицию:

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

Затем вы можете вернуться, например:

fseek(fp, 0L, SEEK_SET);

или (если хотите перейти к началу)

rewind(fp);
person Rob Walker    schedule 26.10.2008
comment
@camh - Спасибо, чувак. Этот комментарий решил мою проблему с алгоритмом определения размера файла. Для записи файл открывается в двоичном режиме, помещая «b» в конец строки режима fopen. - person T.E.D.; 18.05.2010
comment
LOL, да, верно, Windows унаследовала эту глупую чушь с текстовым / двоичным режимом от DOS. В наши дни об этом легко забывают. На самом деле стандарт POSIX даже требует, чтобы любая система POSIX могла справиться с флагом b в вызовах fopen (чтобы быть совместимой со стандартом C!), Но с той же стороны он требует, чтобы реализация полностью игнорировала его, поскольку этот флаг не влияет на системы POSIX (они не знают такой вещи, как текстовый режим и всегда открываются в двоичном режиме). - person Mecki; 09.09.2011
comment
Ага, используйте rewind, прежде чем люди забудут, что это значит - person bobobobo; 23.09.2011
comment
Возвращает подписанное int, поэтому ограничено 2 ГБ. Но с другой стороны, ваш файл может иметь отрицательную длину 2 миллиарда байт, и они готовы к этому. - person Seth; 14.02.2012
comment
Из документации fseek реализациям библиотек разрешено не поддерживать SEEK_END (следовательно, код, использующий у него нет реальной стандартной переносимости). - person Mika Haarahiltunen; 02.09.2013
comment
›Проблем с 2 ГБ можно избежать, используя fseeko и ftello. По возможности отредактируйте ответ. !! - person mk..; 11.04.2014
comment
@MikaHaarahiltunen По крайней мере, если вы работаете в системе POSIX, это это определенно не так. (И я бы вообще не стал доверять cplusplus.com) - person idmean; 11.05.2015
comment
fseek возвращает смещение указателя файла, поэтому вам не нужно использовать ftell. Просто скажите sz = fseek (fp, 0L, SEEK_END) ;. - person Micheal Johnson; 15.12.2015
comment
ЭТО НЕ ПОРТАТИВНО. НЕ ИСПОЛЬЗУЙТЕ ЭТО. ЭТО НЕ СООТВЕТСТВУЕТ POSIX - person Ryan; 12.09.2016
comment
@RobWalker: securecoding.cert.org/confluence/display/c/ - person user2284570; 02.11.2016
comment
@Mecki, потому что Windows была сначала расширением DOS, а не реальной операционной системой - person phuclv; 26.11.2016
comment
Примечание fseek(fp, 0L, SEEK_END); в двоичном потоке не является строго совместимым переносимым кодом C. Согласно сноске 268 стандарта C: Настройка индикатор положения файла до конца файла, как и в случае с fseek (file, 0, SEEK_END), имеет неопределенное поведение для двоичного потока ... и _ 2_ в текстовом потоке не будет работать: Для текстового потока индикатор положения файла содержит неопределенная информация ... необязательно значимая мера количества написанных или прочитанных символов. - person Andrew Henle; 04.05.2018
comment
wiki.sei.cmu.edu/confluence/display/c/ - person SetupX; 18.06.2018
comment
@MichealJohnson, ты имел ввиду lseek() вместо fseek()? На странице руководства, на которую я ссылаюсь, говорится: «После успешного завершения fgetpos(), fseek(), fsetpos() вернут 0. - person tomlogic; 14.04.2020
comment
@tomlogic Кажется, вы правы. Полагаю, я запутал их, поскольку в ответе использовалось fseek. Действительно, на странице руководства для fseek указано, что он вернет 0, а lseek вернет смещение. Кроме того, возвращаясь к коду, в котором я сам использовал эту технику для определения размера файла, я действительно использовал lseek, а не fseek. - person Micheal Johnson; 03.05.2020
comment
@tomlogic Помимо возвращаемого значения, другое основное различие между этими функциями, по-видимому, состоит в том, что одна принимает FILE* дескриптор файла, а другая - int дескриптор файла. Это, как правило, не имеет значения в Linux (если вы согласны с тем, какие функции вы используете, например fopen против open), но у меня были проблемы с переносом программного обеспечения на другие платформы, где функции FILE* поддерживаются, а функции int поддерживаются. нет. Я не знаю подробностей, стоящих за этим, но я предполагаю, что один - это стандарт C, а другой - расширение Linux (или POSIX? Но я думал, что POSIX поддерживается в Windows). - person Micheal Johnson; 03.05.2020
comment
@MichealJohnson: да, fopen() находится в стандартной библиотеке C, а open - из POSIX. stackoverflow.com/a/1658517/266392 отлично справляется с обсуждением различий. - person tomlogic; 05.05.2020
comment
@ VolodymyrM.Lisivka Почему вы добавляете единицу к значению, возвращаемому lseek? Я тестировал его, не добавляя ни одного, и он по-прежнему равен результату stat. Ага, убирать устаревшие комментарии - мое хобби :) - person mathway; 14.06.2021

Используя стандартную библиотеку:

Предполагая, что ваша реализация значимо поддерживает 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.

person Greg Hewgill    schedule 26.10.2008
comment
Обратите внимание, что для ясности я пропустил проверку ошибок. - person Greg Hewgill; 27.10.2008
comment
Имя файла не нужно - для этого можно использовать fstat. - person Tanktalus; 27.10.2008
comment
Вам нужно указать stat на адрес структуры. Вторая строка должна быть: stat (filename, & st); - person Vlad the Impala; 04.11.2009
comment
также можно использовать rewind (f), чтобы переместить указатель файла обратно в начало файла - person Kurru; 17.05.2011
comment
Я пропустил проверку ошибок в интересах -FATAL ERROR, EXITING. - person Buttle Butkus; 03.02.2012
comment
Второй вариант - единственный, который может отображать файлы размером более 2 ГБ. - person Seth; 14.02.2012
comment
замените fseek и ftell на fseeko и ftello. Тогда это также работает для файлов размером более 2 ГБ .. !! - person mk..; 11.04.2014
comment
Вы можете получить дескриптор файла из FILE* с помощью fileno. - person poolie; 01.05.2016
comment
Хорошее решение POSIX. Сделай это! - person kayleeFrye_onDeck; 14.01.2017
comment
Нужна ли бесплатная struct stat st? - person ilw; 16.07.2017
comment
Второй вариант намного лучше! - person Kay Carosfeild; 30.03.2018
comment
ИМХО, пропуск проверки ошибок в этом простом случае делает код опасно некорректным. Например, если имя файла не существует, stat() завершится ошибкой, и никоим образом не ясно, что поле st_size структуры st не содержит мусора стека. По крайней мере, если stat() не удается, доставленный размер должен быть 0. И вы можете включить проверку ошибок, даже не изменяя количество строк в примере: size = (stat(filename, &st) == 0) ? st.st_size : 0;. - person jsbox; 18.11.2019
comment
Примечание. sys/stat.h также доступен в Windows (по крайней мере, для меня в Visual Studio 2019). - person Julian Kirsch; 02.04.2020

Если у вас есть дескриптор файла, 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;
person PiedPiper    schedule 26.10.2008
comment
Добавить fd = fileno (f); если у вас есть поток (например, из fopen), а не дескриптор файла. Требуется проверка ошибок. - person ysth; 27.10.2008
comment
Конечно, это требует проверки ошибок - это только усложнит пример. - person PiedPiper; 27.10.2008
comment
Это, на мой взгляд, лучший реальный ответ, и я думаю, что у всех нас отключены тренировочные колеса по большей части на C, действительно ли нам нужна проверка ошибок и другой ненужный код в наших примерах, это достаточно плохо, что M $ DN делает это в их, давайте не будем следовать их примеру, а просто скажем в конце «обязательно добавьте проверку ошибок» и покончим с этим. - person osirisgothra; 07.11.2013
comment
Если вы вызываете это с помощью fileno (), это может быть неточно из-за кеширования файлов. Я не знаю способа получить длину ФАЙЛА без сброса буфера. - person kainjow; 02.05.2014
comment
МНОЖЕСТВО пользователей SO - студенты C, а не магистры. Следовательно, код, приведенный в ответах, должен отображать проверку ошибок, чтобы учащийся научился правильно кодировать. - person user3629249; 23.02.2015
comment
есть деталь, заключающаяся в том, что (f) stat () возвращает общее количество байтов распределения блоков, в то время как последовательность fseek () / ftell () возвращает количество байтов до обнаружения EOF. - person user3629249; 23.02.2015

В итоге я просто сделал короткую и приятную 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)

person Earlz    schedule 27.03.2011
comment
Хороший момент для восстановления индикатора предыдущей позиции файлового потока. - person Fredrick Gauss; 13.01.2016
comment
ftell(fp) возвращает long. Не нужно, чтобы можно было замкнуть на int и потерять информацию. - person chux - Reinstate Monica; 20.03.2021

Как использовать 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;
}
person Community    schedule 05.01.2010
comment
или if(off == (-1L)) не требуется (long) - person Imobilis; 01.06.2018

Думали ли вы, что не нужно рассчитывать размер файла и при необходимости просто увеличивать массив? Вот пример (без проверки ошибок):

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

Это имеет то преимущество, что работает даже с потоками, в которых невозможно получить размер файла (например, stdin).

person Pat Morin    schedule 29.10.2009
comment
Возможно, здесь можно было бы использовать функцию realloc вместо использования промежуточного указателя и необходимости free(). - person Victor Zamanian; 13.03.2011
comment
У этого есть очень реальный недостаток: O (n ^ 2) ... размер того, что вам нужно скопировать, увеличивается. Хорошо для маленьких файлов, УЖАСНО для больших. Если у вас есть кусок размером 1 КБ и файл размером 100 МБ, вы в конечном итоге скопируете (если я правильно подсчитал) примерно 1E17 байт. Это может быть патологический пример, но он демонстрирует, почему вам не следует этого делать. - person Floris; 28.01.2016
comment
Если я не ошибаюсь, размер каждый раз сохраняется в два раза. Следовательно, время выполнения - O (n), а не O (n ^ 2). Это та же стратегия распределения, которая обычно используется для std :: vector и ему подобных. Тем не менее, перераспределение по-прежнему менее эффективно, чем запрос размера файла и одновременное чтение. - person Joe; 19.04.2016
comment
Это удваивается при каждом перераспределении. Любое изменение размера с постоянным коэффициентом больше единицы достаточно для получения границы O (n), буквальное удвоение может быть излишним, для масштабирования на 1,75, например. используйте np += (np / 2) + (np / 4); - все целые, промежуточные результаты не выходят за рамки преждевременного переполнения. Скорее я бы использовал 1.5, но 1.75 лучше показывает идею. Конечно, следите за переполнением, и, в частности, любое кратное предыдущему размеру может переполниться, когда фактический размер этого не делает. Если размер вашего файла (2^31)-1, вероятно, будет предпринята попытка выделить буфер с -(2^31), а не с 2^31 байтами. - person Steve314; 13.11.2016
comment
Я должен, вероятно, предупредить, что np += (np / 2) + (np / 4) не дает точного умножения на 1,75 - результаты могут быть слишком маленькими, потому что от битов, которые были усечены, не распространяется перенос, - но для этой цели этого должно быть достаточно. Для умножения на 1,5 должно быть правильно np += (np / 2);. - person Steve314; 13.11.2016

Если вы работаете в Linux, серьезно подумайте об использовании g_file_get_contents из glib. Он обрабатывает весь код для загрузки файла, выделения памяти и обработки ошибок.

person Ben Combee    schedule 26.10.2008
comment
Если вы используете Linux и хотите зависеть от glib, то это так. - person JesperE; 27.10.2008
comment
Это не такая уж большая проблема, поскольку сейчас glib используется как приложениями GTK, так и KDE. Он также доступен в Mac OS X и Windows, но там он далеко не стандартный. - person Ben Combee; 29.10.2008
comment
Но разве glib - это не библиотека C ++? Вопрос поставил C - person Dave Appleton; 21.06.2013
comment
@DaveAppleton: Нет, glib - это простая библиотека C, а не C ++. - person Nate C-K; 20.02.2014
comment
@BenCombee glib не на android, последний раз проверял. - person Wyatt Ward; 01.05.2017
comment
Ни GTK, ни KDE по умолчанию не работают на Android. - person Shahe Ansar; 20.05.2017

#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

person plan9assembler    schedule 29.10.2008
comment
Это решение просто излишне сложное и неэффективное. Как ясно из приведенных выше ответов, нет необходимости выполнять команду и анализировать ее вывод. - person brandizzi; 12.03.2011
comment
Кроме того, это решение только для Linux - person bobobobo; 23.09.2011
comment
Лучший ответ. Я нюхаю cat-v.org - person ysh1685; 04.01.2021