Як визначити розмір файлу в C?


137

Як я можу визначити розмір файлу в байтах?

#include <stdio.h>

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

Вам потрібно буде скористатися функцією бібліотеки, щоб отримати деталі файлу. Оскільки C повністю незалежний від платформи, вам потрібно буде повідомити нам, для якої платформи / операційної системи ви розробляєте!
Кріс Робертс

Чому char* file, чому ні FILE* file? -1
Містер Оскар

-1 тому що функції файлів повинні приймати дескриптори файлів, а не файлові шляхи
пан Оскар

Відповіді:


144

На основі коду 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як запропонував Дерек.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

9
Для отримання більш портативного коду використовуйте fseek+, ftellяк запропонував Дерек. Ні. Стандарт C спеціально зазначає, що fseek()для SEEK_ENDбінарного файлу не визначено поведінку. 7.19.9.2 fseekФункція ... Бінарний потік не повинен змістовно підтримувати fseekвиклики з значенням, звідкиSEEK_END зазначено нижче, що знаходиться у виносці 234 на стор. 267 пов'язаних C стандарту, і які специфічно етикетками , fseekщоб SEEK_ENDв довічним потоці в якості невизначеного поведінки. .
Ендрю Генле

74

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

Не використовуйте unsigned int. Файли розміром більше 4 гігабайт поширені як дещо менш рідкісний бруд

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

Якщо у вас є Windows, вам слід скористатися GetFileSizeEx - він фактично використовує підписане 64-бітове ціле число, тож вони почнуть стикатися з проблемами з 8 екзабайтовими файлами. Дурний Microsoft! :-)


1
Я використовував компілятори, де off_t - 32 біта. Зрозуміло, це відбувається у вбудованих системах, де файли 4 Гб є менш поширеними. У будь-якому випадку, POSIX також визначає off64_t та відповідні методи для додавання в плутанину.
Аарон Кемпбелл

Я завжди люблю відповіді, які передбачають Windows, і нічого іншого, крім критики цього питання, не люблю. Не могли б ви додати щось, що відповідає POSIX?
СС Енн

1
@ JL2210 прийнята відповідь від Теда Персіваля показує рішення, сумісне з позицією, тому я не бачу сенсу повторювати очевидне. Я (та ще 70 осіб) думали, що додавання примітки про Windows та не використовувати підписані 32-бітові цілі числа для представлення розмірів файлів - це додаткове значення. Ура
Оріон Едвардс

30

Рішення Метта повинно працювати, за винятком того, що це 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;
}

Фіксований брекет теж для вас. ;)

Оновлення: Це насправді не найкраще рішення. Він обмежений 4 Гб файлами в Windows, і, ймовірно, повільніше, ніж просто використання виклику, який залежить від платформи, як GetFileSizeExабо stat64.


Так, слід. Однак, якщо немає дійсно переконливої ​​причини не писати певну платформу, вам, ймовірно, слід просто використовувати конкретний виклик платформи, а не шаблон відкритого / шукаючого, скажу / закритого.
Дерек Парк

1
Вибачте за пізню відповідь, але у мене тут є головне питання. Це призводить до того, що програма зависає під час доступу до файлів з обмеженим доступом (наприклад, захищених паролем або системних файлів). Чи є спосіб запитати у користувача пароль, коли це потрібно?
Джастін

@Justin, ви, мабуть, вам слід відкрити нове запитання, зокрема про проблему, з якою ви стикаєтесь, та надати детальну інформацію про платформу, на якій ви працюєте, як ви отримуєте доступ до файлів та про поведінку.
Дерек Парк

1
Обидва C99 і C11 повернення long intз ftell(). (unsigned long)лиття не покращує діапазон, оскільки вже обмежений функцією. ftell()повернути -1 на помилку, і це заплутається з актором. Запропонувати fsize()повернути той самий тип, що і ftell().
chux

Я згоден. Акторський склад повинен був відповідати оригінальному прототипу у питанні. Я не можу пригадати, чому я перетворив це на безпідписаний довгий замість непідписаний int.
Дерек Парк

15

** Не робіть цього ( чому? ):

Цитуючи стандартну документацію 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
}

5
@mezhaka: Цей звіт CERT просто неправильний. fseekoі ftello(або fseekі ftellякщо ви застрягли без першого і щасливим з обмеженнями на розміри файлів ви можете працювати з) , є правильним способом , щоб визначити довжину файлу. statРішення на базі не працюють у багатьох "файлах" (наприклад, блокових пристроях) і не переносяться на системи, які не є POSIX.
R .. GitHub СТОП ДОПОМОГАТИ ICE

1
Це єдиний спосіб отримати розмір файлу в багатьох системах, що не відповідають стандартам (наприклад, мій дуже мінімалістичний 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

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


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

24
Це не є стандартом C. Це частина стандарту POSIX, але не є стандартом C.
Дерек Парк

3

Швидкий пошук в Google знайшов метод, що використовує fseek і ftell, а нитку з цим питанням дають відповіді, що це неможливо зробити просто на C по-іншому.

Ви можете використовувати бібліотеку портативності на зразок NSPR (бібліотеку, що забезпечує Firefox) або перевірити її реалізацію (досить волохату).


1

Я використовував цей набір коду, щоб знайти довжину файлу.

//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);

1

Спробуйте це --

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

Для цього потрібно спершу донестись до кінця файлу; потім повідомте, де знаходиться покажчик файлу. Нарешті (це необов'язково) він перемотується назад на початок файлу. Зауважте, щоfp повинен бути двійковий потік.

file_size містить кількість байтів, які містить файл. Зауважте, що оскільки (відповідно до climits.h) довгий неподписаний тип обмежений 4294967295 байтами (4 гігабайтами), вам потрібно буде знайти інший тип змінної, якщо ви, ймовірно, маєте справу з файлами, більшими за це.


3
Чим це відрізняється від відповіді Дерека від 8 років тому?
ПП

Це невизначена поведінка для бінарного потоку та для текстового потоку 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, але думаю, що це має працювати.


12
Вам не потрібно було б визначати щось на зразок SEEKBOTTOM. #include <stdio.h> fseek (ручка, 0, SEEK_END);
sigjuice

-4

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

  long size = ftell(FILENAME);
  printf("total size is %ld bytes",size);

ftellочікує дескриптор файлу, а не ім'я файлу як аргумент.
Бармар

@Barmar, Ні ftellне очікує дескриптора файлу, він очікує FILE*замість цього. Перегляньте першу сторінку чоловіка!

Підхід абсолютно невірний, він постійно ftellповертається 0!

Ця відповідь виправдана неправильно, як для одного, вам потрібно спочатку скористатися, fseek()щоб шукати кінець файлу, а також, ftell()очікує FILE *, що не, а рядок! Вам би добре послужили, щоб уточнити свою відповідь.
Містер Оскар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.