Come si determina la dimensione di un file in C?


137

Come posso capire la dimensione di un file, in byte?

#include <stdio.h>

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

Dovrai utilizzare una funzione di libreria per recuperare i dettagli di un file. Dato che C è completamente indipendente dalla piattaforma, dovrai farci sapere per quale piattaforma / sistema operativo stai sviluppando!
Chris Roberts,

Perché char* file, perché no FILE* file? -1
Oscar

-1 perché le funzioni dei file dovrebbero accettare descrittori di file e non percorsi di file
Sig. Oscar

Risposte:


144

Basato sul codice di 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; 
}

I cambiamenti:

  • Crea l'argomento nome file a const char.
  • Correzione della struct statdefinizione, in cui mancava il nome della variabile.
  • Restituisce -1in caso di errore anziché 0, il che sarebbe ambiguo per un file vuoto. off_tè un tipo firmato quindi questo è possibile.

Se si desidera fsize()stampare un messaggio in caso di errore, è possibile utilizzare questo:

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

Sui sistemi a 32 bit è necessario compilarlo con l'opzione -D_FILE_OFFSET_BITS=64, altrimenti off_tconterrà solo valori fino a 2 GB. Per i dettagli, consultare la sezione "Uso di LFS" del supporto per file di grandi dimensioni in Linux .


19
Questo è specifico per Linux / Unix - probabilmente vale la pena sottolinearlo poiché la domanda non specificava un sistema operativo.
Ha disegnato la sala il

1
Probabilmente potresti cambiare il tipo di ritorno in ssize_t e lanciare la dimensione da un off_t senza alcun problema. Sembrerebbe più sensato usare un ssize_t :-) (da non confondere con size_t che non è firmato e non può essere usato per indicare un errore.)
Ted Percival

1
Per un codice più portatile, utilizzare fseek+ ftellcome proposto da Derek.
Ciro Santilli 2 冠状 病 六四 事件 法轮功

9
Per un codice più portatile, utilizzare fseek+ ftellcome proposto da Derek. No. Il C standard prevede espressamente che fseek()ad SEEK_ENDun file binario è un comportamento indefinito. 7.19.9.2 La fseekfunzione ... Un flusso binario non deve necessariamente supportare in modo significativo le fseekchiamate con un valore diSEEK_END , e come indicato di seguito, che è dalla nota 234 a pag. 267 della collegata C standard, e che in particolare etichette fseeka SEEK_ENDin un flusso binario come comportamento indefinito. .
Andrew Henle,

74

Non usare int. I file di dimensioni superiori a 2 gigabyte sono comuni come sporcizia in questi giorni

Non usare unsigned int. I file di dimensioni superiori a 4 gigabyte sono comuni come sporcizia leggermente meno comune

IIRC la libreria standard definisce off_tcome un intero a 64 bit senza segno, che è quello che tutti dovrebbero usare. Possiamo ridefinirlo in 128 anni in pochi anni quando iniziamo ad avere 16 file exabyte in giro.

Se sei su Windows, dovresti usare GetFileSizeEx : in realtà utilizza un intero con segno a 64 bit, quindi inizieranno a colpire i problemi con 8 file exabyte. Microsoft sciocca! :-)


1
Ho usato compilatori in cui off_t è 32 bit. Certo, questo è su sistemi embedded in cui i file da 4 GB sono meno comuni. Comunque, POSIX definisce anche off64_t e i metodi corrispondenti da aggiungere alla confusione.
Aaron Campbell,

Amo sempre le risposte che presuppongono Windows e non fanno altro che criticare la domanda. Potresti aggiungere qualcosa conforme a POSIX?
SS Anne,

1
@ JL2210 la risposta accettata da Ted Percival mostra una soluzione conforme a posix, quindi non vedo senso ripetere l'ovvio. Io (e altri 70) ho pensato che aggiungere una nota su Windows e non usare numeri interi a 32 bit con segno per rappresentare le dimensioni dei file fosse un valore aggiunto. Saluti
Orion Edwards,

30

La soluzione di Matt dovrebbe funzionare, tranne per il fatto che è C ++ anziché C, e la spiegazione iniziale non dovrebbe essere necessaria.

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

Risolto il problema anche con te. ;)

Aggiornamento: questa non è davvero la soluzione migliore. È limitato a file da 4 GB su Windows ed è probabilmente più lento del semplice utilizzo di una chiamata specifica della piattaforma come GetFileSizeExo stat64.


Si, dovresti. Tuttavia, a meno che non ci sia un motivo davvero convincente non specifico per la piattaforma, tuttavia, probabilmente dovresti semplicemente usare una chiamata specifica per la piattaforma piuttosto che il modello open / seek-end / tell / close.
Derek Park,

1
Mi dispiace per la risposta tardiva, ma sto riscontrando un grosso problema qui. Fa bloccare l'app quando si accede a file con restrizioni (come file protetti da password o file di sistema). C'è un modo per chiedere all'utente una password quando necessario?
Justin

@Justin, probabilmente dovresti aprire una nuova domanda specificamente sul problema che stai riscontrando e fornire dettagli sulla piattaforma in cui ti trovi, su come stai accedendo ai file e qual è il comportamento.
Derek Park,

1
Sia C99 che C11 ritornano long intda ftell(). (unsigned long)il casting non migliora l'intervallo in quanto già limitato dalla funzione. ftell()restituisce -1 in caso di errore e viene offuscato dal cast. Suggerisci fsize()restituire lo stesso tipo di ftell().
chux - Ripristina Monica il

Sono d'accordo. Il cast doveva corrispondere al prototipo originale nella domanda. Non ricordo però perché l'ho trasformato in unsigned long anziché in unsigned int.
Derek Park,

15

** Non farlo ( perché? ):

Citando il documento standard C99 che ho trovato online: "L'impostazione dell'indicatore di posizione del file su end-of-file, come con fseek(file, 0, SEEK_END), ha un comportamento indefinito per un flusso binario (a causa di possibili caratteri null finali) o per qualsiasi flusso con codifica dipendente dallo stato che sicuramente non termina nello stato iniziale del turno. **

Modificare la definizione in int in modo che i messaggi di errore possano essere trasmessi, quindi utilizzare fseek()e ftell()per determinare la dimensione del file.

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: quel rapporto CERT è semplicemente sbagliato. fseekoe ftello(o fseeke ftellse sei bloccato senza il primo e soddisfatto dei limiti sulle dimensioni dei file con cui puoi lavorare) sono il modo corretto di determinare la lunghezza di un file. statle soluzioni basate su base non funzionano su molti "file" (come i dispositivi a blocchi) e non sono portabili su sistemi non POSIX.
R .. GitHub FERMA AIUTANDO ICE

1
Questo è l'unico modo per ottenere le dimensioni del file su molti sistemi non posix (come il mio mbed molto minimalista)
Earlz

9

POSIX

Lo standard POSIX ha il suo metodo per ottenere le dimensioni del file.
Includi l' sys/stat.hintestazione per usare la funzione.

Sinossi

  • Ottieni statistiche sui file utilizzando stat(3).
  • Ottieni la st_sizeproprietà.

Esempi

Nota : limita la dimensione a 4GB. Se non il Fat32filesystem, usa la versione a 64 bit!

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

L' ANSI C non fornisce direttamente il modo per determinare la lunghezza del file.
Dovremo usare la nostra mente. Per ora, useremo l'approccio seek!

Sinossi

  • Cerca il file fino alla fine usando fseek(3).
  • Ottieni la posizione corrente usando ftell(3).

Esempio

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

Se il file è stdino una pipe. POSIX, ANSI C non funzionerà.
Restituirà 0se il file è una pipe o stdin.

Opinione : si dovrebbe usare invece lo standard POSIX . Perché, ha il supporto a 64 bit.


1
struct _stat64e __stat64()per _Windows.
Bob Stein,

5

E se stai creando un'app di Windows, usa l' API GetFileSizeEx poiché l'I / O dei file CRT è disordinato, soprattutto per determinare la lunghezza del file, a causa delle peculiarità nelle rappresentazioni dei file su sistemi diversi;)


5

Se stai bene usando la libreria 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
Non è lo standard C. Fa parte dello standard POSIX, ma non lo standard C.
Derek Park,


1

Ho usato questo set di codice per trovare la lunghezza del file.

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

Prova questo --

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

Quello che fa è prima di tutto, cerca fino alla fine del file; quindi, segnala dove si trova il puntatore del file. Infine (questo è facoltativo) si riavvolge all'inizio del file. Si noti che fpdovrebbe essere un flusso binario.

file_size contiene il numero di byte contenuti nel file. Si noti che poiché (in base a climits.h) il tipo lungo senza segno è limitato a 4294967295 byte (4 gigabyte), è necessario trovare un tipo di variabile diverso se si rischia di gestire file di dimensioni maggiori.


3
In che modo differisce dalla risposta di Derek di 8 anni fa?
PP,

Questo è un comportamento indefinito per un flusso binario e per un flusso di testo ftellnon restituisce un valore rappresentativo del numero di byte che è possibile leggere dal file.
Andrew Henle,

0

Ho una funzione che funziona bene solo con stdio.h. Mi piace molto e funziona molto bene ed è piuttosto conciso:

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

0

Ecco una funzione semplice e pulita che restituisce le dimensioni del file.

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
Non è necessario chiudere il file?
Jerry Jeremiah,

No, non mi piacciono le funzioni che prevedono un percorso. Invece, ti prego di farti sfuggire un puntatore a file
Mr Oscar

-3

È possibile aprire il file, andare a 0 offset rispetto dalla parte inferiore del file con

#define SEEKBOTTOM   2

fseek(handle, 0, SEEKBOTTOM)  

il valore restituito da fseek è la dimensione del file.

Non ho programmato il codice in C per molto tempo, ma penso che dovrebbe funzionare.


12
Non dovresti definire qualcosa come SEEKBOTTOM. #include <stdio.h> fseek (handle, 0, SEEK_END);
sigjuice,

-4

Osservando la domanda, è ftellpossibile ottenere facilmente il numero di byte.

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

ftellsi aspetta un descrittore di file, non un nome file, come argomento.
Barmar,

@Barmar, No ftellnon si aspetta un descrittore di file, si aspetta FILE*invece un . Vedi prima la pagina man!

L'approccio è completamente sbagliato, è costante che ftelltornerebbe 0ogni volta!

Questa risposta è completamente sbagliata, come per uno, è necessario utilizzare fseek()prima per cercare la fine del file e, inoltre, si ftell()aspetta un FILE *, non una stringa! Saresti ben servito per dare corpo alla tua risposta.
Oscar
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.