Jak określasz rozmiar pliku w C?


137

Jak mogę obliczyć rozmiar pliku w bajtach?

#include <stdio.h>

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

Będziesz musiał użyć funkcji biblioteki, aby pobrać szczegóły pliku. Ponieważ C jest całkowicie niezależny od platformy, musisz nam powiedzieć, dla jakiej platformy / systemu operacyjnego tworzysz!
Chris Roberts,

Dlaczego char* filenie FILE* file? -1
Pan Oscar,

-1, ponieważ funkcje plików powinny akceptować deskryptory plików, a nie ścieżki do plików
Pan Oscar

Odpowiedzi:


144

Na podstawie kodu 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; 
}

Zmiany:

  • Zmieniono argument nazwa pliku na const char.
  • Poprawiono struct statdefinicję, w której brakowało nazwy zmiennej.
  • Zwraca -1w przypadku błędu zamiast 0, co byłoby niejednoznaczne dla pustego pliku. off_tjest typem ze znakiem, więc jest to możliwe.

Jeśli chcesz fsize()wydrukować komunikat o błędzie, możesz użyć tego:

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

W systemach 32-bitowych należy to skompilować z opcją -D_FILE_OFFSET_BITS=64, w przeciwnym razie off_tbędą przechowywane tylko wartości do 2 GB. Szczegółowe informacje można znaleźć w sekcji „Korzystanie z LFS” w dokumencie Obsługa dużych plików w systemie Linux .


19
Jest to specyficzne dla systemu Linux / Unix - prawdopodobnie warto na to zwrócić uwagę, ponieważ pytanie nie określało systemu operacyjnego.
Drew Hall

1
Prawdopodobnie możesz zmienić typ zwracania na ssize_t i rzutować rozmiar z off_t bez żadnych problemów. Wydaje się, że bardziej sensowne byłoby użycie ssize_t :-) (nie mylić z size_t, który jest bez znaku i nie może być użyty do wskazania błędu.)
Ted Percival

1
Aby uzyskać bardziej przenośny kod, użyj fseek+ ftellzgodnie z propozycją Dereka.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

9
Aby uzyskać bardziej przenośny kod, użyj fseek+ ftellzgodnie z propozycją Dereka. Nie C Standardowy wyraźnie stanowi, że fseek()aby SEEK_ENDna plik binarny jest niezdefiniowane zachowanie. 7.19.9.2 fseekFunkcja ... Strumień binarny nie musi w znaczący sposób obsługiwać fseekwywołań o wartości pochodzeniaSEEK_END i, jak wspomniano poniżej, z przypisu 234 na str. 267 połączonego C standardowa, która specyficznie etykiet fseekdo SEEK_ENDw binarnego strumienia niezdefiniowana zachowanie. .
Andrew Henle,

74

Nie używaj int. Pliki o rozmiarze ponad 2 gigabajtów są obecnie powszechne jako brud

Nie używaj unsigned int. Pliki o rozmiarze powyżej 4 gigabajtów są powszechne jako nieco mniej powszechny brud

IIRC biblioteka standardowa definiuje off_tjako 64-bitową liczbę całkowitą bez znaku, której każdy powinien używać. Możemy przedefiniować to na 128 bitów w ciągu kilku lat, kiedy zaczniemy trzymać w pobliżu 16 eksabajtów.

Jeśli korzystasz z systemu Windows, powinieneś użyć GetFileSizeEx - w rzeczywistości używa on 64-bitowej liczby całkowitej ze znakiem, więc zaczną napotykać problemy z 8 plikami eksabajtowymi. Głupi Microsoft! :-)


1
Użyłem kompilatorów, w których off_t to 32 bity. To prawda, dotyczy to systemów wbudowanych, w których pliki 4 GB są mniej powszechne. W każdym razie POSIX definiuje również off64_t i odpowiednie metody, które mogą być przyczyną zamieszania.
Aaron Campbell

Zawsze uwielbiam odpowiedzi, które zakładają Windows i nie robię nic poza krytyką pytania. Czy mógłbyś dodać coś, co jest zgodne z POSIX?
SS Anne

1
@ JL2210 zaakceptowana odpowiedź od Teda Percivala pokazuje rozwiązanie zgodne z Posix, więc nie widzę sensu powtarzania tego, co oczywiste. Ja (i 70 innych) uważałem, że dodanie uwagi o oknach i niestosowanie 32-bitowych liczb całkowitych ze znakiem do reprezentowania rozmiarów plików było dodatkowym dodatkiem. Pozdrawiam
Orion Edwards

30

Rozwiązanie Matta powinno działać, z tym wyjątkiem, że jest to C ++ zamiast C, a początkowe polecenie tell nie powinno być konieczne.

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

Naprawiono też aparat ortodontyczny. ;)

Aktualizacja: to naprawdę nie jest najlepsze rozwiązanie. Jest ograniczony do plików 4 GB w systemie Windows i prawdopodobnie działa wolniej niż zwykłe wywołanie specyficzne dla platformy, takie jak GetFileSizeExlub stat64.


Tak, powinieneś. Jednak jeśli nie ma naprawdę ważnego powodu, aby nie pisać specyficznej dla platformy, prawdopodobnie powinieneś po prostu użyć wywołania specyficznego dla platformy, a nie wzorca open / seek-end / tell / close.
Derek Park

1
Przepraszam za późną odpowiedź, ale mam tutaj poważny problem. Powoduje zawieszanie się aplikacji podczas uzyskiwania dostępu do plików z ograniczeniami (takich jak pliki chronione hasłem lub pliki systemowe). Czy w razie potrzeby można poprosić użytkownika o hasło?
Justin

@Justin, prawdopodobnie powinieneś otworzyć nowe pytanie dotyczące konkretnego problemu i podać szczegółowe informacje na temat platformy, na której się znajdujesz, sposobu uzyskiwania dostępu do plików i zachowania.
Derek Park

1
Zarówno C99, jak i C11 powracają long intz ftell(). (unsigned long)rzucanie nie poprawia zasięgu, który jest już ograniczony przez funkcję. ftell()zwraca -1 w przypadku błędu i to zostaje zaciemnione przy rzutowaniu. Zaproponuj fsize()zwrot tego samego typu coftell() .
chux - Przywróć Monikę

Zgadzam się. Obsada miała pasować do oryginalnego prototypu w pytaniu. Nie pamiętam jednak, dlaczego zmieniłem go na unsigned long zamiast unsigned int.
Derek Park

15

** Nie rób tego ( dlaczego? ):

Cytując standardowy dokument C99, który znalazłem online: „Ustawienie wskaźnika pozycji pliku na koniec pliku, tak jak w przypadku fseek(file, 0, SEEK_END), ma niezdefiniowane zachowanie dla strumienia binarnego (z powodu możliwych końcowych znaków zerowych) lub dowolnego strumienia z kodowaniem zależnym od stanu to z pewnością nie kończy się w początkowym stanie zmiany. **

Zmień definicję na int, aby można było przesyłać komunikaty o błędach, a następnie użyj fseek()i, ftell()aby określić rozmiar pliku.

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: Ten raport CERT jest po prostu błędny. fseekoi ftello(lub fseeki ftelljeśli utkniesz bez poprzedniego i zadowolony z ograniczeniami rozmiary plików można pracować z) jest poprawny sposób określenia długości pliku. statrozwiązania oparte na systemie nie działają na wielu "plikach" (takich jak urządzenia blokowe) i nie są przenośne do systemów innych niż POSIX-ish.
R .. GitHub STOP HELPING ICE

1
To jedyny sposób, aby uzyskać rozmiar pliku na wielu systemach niezgodnych z POSIX (takich jak mój bardzo minimalistyczny mbed)
Earlz

9

POSIX

Standard POSIX ma własną metodę uzyskiwania rozmiaru pliku.
Dołącz sys/stat.hnagłówek, aby użyć funkcji.

Streszczenie

  • Uzyskaj statystyki plików za pomocą stat(3) .
  • Uzyskaj st_sizenieruchomość.

Przykłady

Uwaga : ogranicza rozmiar do 4GB. Jeśli nie jest to Fat32system plików, użyj wersji 64-bitowej!

#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 (standard)

ANSI C. bezpośrednio nie zapewnia drogę do określenia długości pliku.
Będziemy musieli użyć naszego umysłu. Na razie użyjemy metody wyszukiwania!

Streszczenie

  • Wyszukaj plik do końca za pomocą fseek(3) .
  • Uzyskaj aktualną pozycję za pomocą ftell(3).

Przykład

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

Jeśli plik to stdinlub potok. POSIX, ANSI C nie będzie działać.
Zwróci, 0jeśli plik jest potokiem lub stdin.

Opinia : Zamiast tego powinieneś użyć standardu POSIX . Ponieważ ma obsługę 64-bitową.


1
struct _stat64i __stat64()dla _Windows.
Bob Stein

5

A jeśli tworzysz aplikację dla Windows, użyj GetFileSizeEx API, ponieważ I / O pliku CRT jest niechlujne, szczególnie przy określaniu długości pliku, ze względu na osobliwości reprezentacji plików na różnych systemach;)


5

Jeśli nie masz nic przeciwko korzystaniu z biblioteki 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
To nie jest standard C. Jest częścią standardu POSIX, ale nie jest standardem C.
Derek Park


1

Użyłem tego zestawu kodu, aby znaleźć długość pliku.

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

Spróbuj tego --

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

To, co to robi, to najpierw szukanie do końca pliku; następnie zgłoś, gdzie znajduje się wskaźnik pliku. Na koniec (jest to opcjonalne) przewija się z powrotem do początku pliku. Zwróć na to uwagęfp powinien to być strumień binarny.

file_size zawiera liczbę bajtów, które zawiera plik. Zwróć uwagę, że ponieważ (według climits.h) typ long unsigned jest ograniczony do 4294967295 bajtów (4 gigabajty), musisz znaleźć inny typ zmiennej, jeśli masz do czynienia z plikami większymi niż to.


3
Czym to się różni od odpowiedzi Dereka sprzed 8 lat?
PP

Jest to niezdefiniowane zachowanie dla strumienia binarnego, a dla strumienia tekstowego ftellnie zwraca wartości reprezentującej liczbę bajtów, które można odczytać z pliku.
Andrew Henle

0

Mam funkcję, która działa dobrze tylko z stdio.h. Bardzo mi się podoba i działa bardzo dobrze i jest dość zwięzły:

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

0

Oto prosta i przejrzysta funkcja, która zwraca rozmiar pliku.

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
Nie musisz zamykać pliku?
Jerry Jeremiah

Nie, nie lubię funkcji, które oczekują ścieżki. Zamiast tego, spodziewaj się wskaźnika pliku
Pan Oscar

-3

Możesz otworzyć plik, przejść do przesunięcia 0 względem dołu pliku za pomocą

#define SEEKBOTTOM   2

fseek(handle, 0, SEEKBOTTOM)  

wartość zwracana przez fseek to rozmiar pliku.

Długo nie kodowałem w C, ale myślę, że powinno działać.


12
Nie powinieneś musieć definiować czegoś takiego jak SEEKBOTTOM. #include <stdio.h> fseek (uchwyt, 0, SEEK_END);
sigjuice

-4

Patrząc na pytanie, ftellmożna łatwo uzyskać liczbę bajtów.

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

ftelloczekuje deskryptora pliku, a nie nazwy pliku jako argumentu.
Barmar

@Barmar, No ftellnie oczekuje deskryptora pliku, oczekuje FILE*zamiast tego. Najpierw zobacz stronę podręcznika!

Podejście jest całkowicie błędne, jest stałe, ftellże powróci 0za każdym razem!

Ta odpowiedź jest całkowicie błędna, ponieważ po pierwsze, musisz użyć fseek()najpierw do wyszukania końca pliku, a także ftell()oczekuje , że FILE *nie ma ciągu! Dobrze by było, gdybyś uściślił swoją odpowiedź.
Pan Oscar
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.