Как определить размер файла на C?

142

Как я могу определить размер файла в байтах?

#include <stdio.h>

unsigned int fsize(char* file){
  //what goes here?
}
Эндрюрк
источник
Вам нужно будет использовать библиотечную функцию для получения сведений о файле. Поскольку C полностью независим от платформы, вам нужно будет сообщить нам, для какой платформы / операционной системы вы разрабатываете!
Крис Робертс,
Почему char* file, почему нет FILE* file? -1
-1, потому что файловые функции должны принимать дескрипторы файлов, а не пути к файлам

Ответы:

147

На основе кода 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; 
}

Изменения:

  • Сделал аргумент имени файла a 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 ГБ. См. Подробности в разделе «Использование LFS» в статье « Поддержка больших файлов в Linux» .

Тед Персиваль
источник
19
Это специфично для Linux / Unix - вероятно, стоит указать на это, поскольку в вопросе не указана ОС.
Дрю Холл
1
Вероятно, вы могли бы без проблем изменить возвращаемый тип на ssize_t и преобразовать размер из off_t. Казалось бы, имеет смысл использовать ssize_t :-) (не путать с size_t, которое не имеет знака и не может использоваться для обозначения ошибки.)
Тед Персиваль
1
Для более переносимого кода используйте fseek+, ftellпредложенный Дереком.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
9
Для более переносимого кода используйте fseek+, ftellпредложенный Дереком. Нет. Стандарт C специально указывает, что fseek()для SEEK_ENDдвоичного файла поведение undefined. 7.19.9.2 fseekФункция ... Двоичный поток не обязательно должен поддерживать fseekвызовы со значением источникаSEEK_END , равным , как указано ниже, из сноски 234 на стр. 267 связанных C стандарта, и которые специфически этикетками , fseekчтобы SEEK_ENDв двоичном потоке в качестве неопределенного поведения. .
Эндрю Хенле
Из руководства gnu libc : ... Системы [не-POSIX] делают различие между файлами, содержащими текст, и файлами, содержащими двоичные данные, и средства ввода и вывода ISO C предусматривают это различие. ... В библиотеке GNU C и во всех системах POSIX нет разницы между текстовыми потоками и двоичными потоками. Когда вы открываете поток, вы получаете тот же поток независимо от того, запрашиваете ли вы двоичный файл. Этот поток может обрабатывать любое содержимое файла и не имеет ограничений, которые иногда имеют текстовые потоки.
Маленький мальчик,
75

Не используйте int. Файлы размером более 2 гигабайт в наши дни стали обычным явлением

Не используйте unsigned int. Файлы размером более 4 гигабайт обычно встречаются как немного менее распространенная грязь.

IIRC стандартная библиотека определяет off_tкак 64-битное целое число без знака, которое следует использовать всем. Мы можем переопределить это значение до 128 бит через несколько лет, когда мы начнем иметь файлы размером 16 эксабайт.

Если вы работаете в Windows, вам следует использовать GetFileSizeEx - он фактически использует 64-битное целое число со знаком, поэтому они начнут сталкиваться с проблемами с 8-ми эксабайтными файлами. Глупый Microsoft! :-)

Орион Эдвардс
источник
2
Я использовал компиляторы, где off_t - 32 бита. Конечно, это во встроенных системах, где файлы размером 4 ГБ встречаются реже. В любом случае, POSIX также определяет off64_t и соответствующие методы, чтобы добавить путаницы.
Аарон Кэмпбелл,
Мне всегда нравятся ответы, которые предполагают наличие Windows и ничего не делают, кроме критики вопроса. Не могли бы вы добавить что-нибудь, совместимое с POSIX?
SS Anne
1
@ JL2210 принятый ответ от Теда Персиваля показывает решение, совместимое с posix, поэтому я не вижу смысла повторять очевидное. Я (и еще 70 человек) подумал, что добавление примечания об окнах и отказ от использования 32-битных целых чисел со знаком для представления размеров файлов было добавлением ценности к этому. Ура
Орион Эдвардс
31

Решение Мэтта должно работать, за исключением того, что это 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 он ограничен файлами размером 4 ГБ и, вероятно, медленнее, чем простой вызов для конкретной платформы, например GetFileSizeExили stat64.

Дерек Парк
источник
Да, ты должен. Однако, если нет действительно веской причины не писать для конкретной платформы, вам, вероятно, следует просто использовать вызов для конкретной платформы, а не шаблон open / seek-end / tell / close.
Дерек Парк
1
Извините за поздний ответ, но у меня серьезная проблема. Это приводит к зависанию приложения при доступе к файлам с ограниченным доступом (например, защищенным паролем или системным файлам). Есть ли способ при необходимости запросить пароль у пользователя?
Justin
@Justin, вам, вероятно, следует открыть новый вопрос конкретно о проблеме, с которой вы столкнулись, и предоставить подробную информацию о платформе, на которой вы находитесь, о том, как вы получаете доступ к файлам и каково поведение.
Дерек Парк
1
И C99, и C11 возвращаются long intиз ftell(). (unsigned long)приведение не улучшает диапазон, который уже ограничен функцией. ftell()вернуть -1 при ошибке, и это будет запутано с приведением. Предлагаю fsize()вернуть тот же тип, что и ftell().
chux
Согласен. Актерский состав должен был соответствовать оригинальному прототипу в вопросе. Я не могу вспомнить, почему я превратил его в unsigned long вместо unsigned int.
Дерек Парк
14

** Не делайте этого ( почему? ):

Цитата из стандартного документа 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
}
andrewrk
источник
5
@mezhaka: Этот отчет CERT просто неверен. fseekoи ftello(или fseekи ftellесли вы застряли без первого и счастливым с ограничениями на размеры файлов вы можете работать с) , являются правильным способом , чтобы определить длину файла. statРешения на основе не работают со многими «файлами» (такими как блочные устройства) и не переносятся на системы, отличные от POSIX.
R .. GitHub НЕ ПОМОГАЕТ ICE
1
Это единственный способ получить размер файла во многих системах, несовместимых с posix (например, в моем очень минималистичном mbed)
Earlz
9

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 непосредственно не обеспечивает способ определить длину файла.
Придется использовать свой разум. А пока воспользуемся методом поиска!

Синопсис

  • Ищите файл до конца, используя 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 работать не будут.
Он вернется, 0если файл является каналом или stdin.

Мнение : Вместо этого вам следует использовать стандарт POSIX . Потому что он поддерживает 64-битную версию.


источник
1
struct _stat64и __stat64()для _Windows.
Боб Штейн
5

Если вас устраивает библиотека 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;
}
NilObject
источник
24
Это не стандартный C. Это часть стандарта POSIX, но не стандарта C.
Дерек Парк,
4

А если вы создаете приложение для Windows, используйте GetFileSizeEx API, поскольку ввод-вывод файла CRT беспорядочный, особенно для определения длины файла, из-за особенностей представления файлов в разных системах;)


источник
3

Быстрый поиск в Google нашел метод, использующий fseek и ftell, и ветку с этим вопросом с ответами, что это невозможно сделать на одном языке C другим способом.

Вы можете использовать библиотеку переносимости, такую ​​как NSPR (библиотека, на которой работает Firefox), или проверить ее реализацию (довольно сложно).

Николай
источник
2

Я использовал этот набор кода, чтобы определить длину файла.

//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);
rco16
источник
0

Попробуй это --

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

Сначала выполняется поиск до конца файла; затем сообщите, где находится указатель файла. Наконец (это необязательно) он перематывает назад к началу файла. Обратите внимание, что это fpдолжен быть двоичный поток.

file_size содержит количество байтов, содержащихся в файле. Обратите внимание, что, поскольку (согласно climits.h) тип unsigned long ограничен 4294967295 байтами (4 гигабайта), вам нужно будет найти другой тип переменной, если вы, вероятно, будете иметь дело с файлами большего размера.

Адриан
источник
3
Чем это отличается от ответа Дерека 8 лет назад?
PP
Это неопределенное поведение для двоичного потока, а для текстового потока ftellне возвращается значение, представляющее количество байтов, которые могут быть прочитаны из файла.
Эндрю Хенле
0

У меня есть функция, которая хорошо работает только с stdio.h. Мне он очень нравится, работает очень хорошо и довольно лаконично:

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

источник
0

Вот простая и понятная функция, которая возвращает размер файла.

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); 
    fp.close();
    return 
}
Абдессамад Даугри
источник
1
Не нужно ли закрывать файл?
Джерри Иеремия
Нет, мне не нравятся функции, ожидающие пути. Вместо этого, пожалуйста, сделайте так, чтобы указатель на файл был
-3

Вы можете открыть файл, перейти к 0 смещению относительно нижнего края файла с помощью

#define SEEKBOTTOM   2

fseek(handle, 0, SEEKBOTTOM)  

значение, возвращаемое из fseek, - это размер файла.

Я давно не кодировал на C, но думаю, что это должно сработать.

PabloG
источник
12
Вам не нужно определять что-то вроде SEEKBOTTOM. # включить <stdio.h> fseek (дескриптор, 0, SEEK_END);
sigjuice
-3

Глядя на вопрос, ftellлегко можно получить количество байтов.

  long size = ftell(FILENAME);
  printf("total size is %ld bytes",size);
Zishan
источник
ftellожидает в качестве аргумента дескриптор файла, а не имя файла.
Barmar
@Barmar, Нет ftellне ожидает файловый дескриптор, FILE*вместо этого ожидает . Сначала см. Справочную страницу!
Подход совершенно неправильный, это постоянно, что ftellбудет возвращаться 0каждый раз!
Этот ответ совершенно неверен, так как, во- fseek()первых , вам нужно использовать сначала для поиска конца файла, а также ftell()ожидает, а FILE *не строку! Вам будет полезно сформулировать свой ответ.