Wie bestimmen Sie die Größe einer Datei in C?

137

Wie kann ich die Größe einer Datei in Bytes ermitteln?

#include <stdio.h>

unsigned int fsize(char* file){
  //what goes here?
}
andrewrk
quelle
Sie müssen eine Bibliotheksfunktion verwenden, um die Details einer Datei abzurufen. Da C vollständig plattformunabhängig ist, müssen Sie uns mitteilen, für welche Plattform / welches Betriebssystem Sie entwickeln!
Chris Roberts
Warum char* file, warum nicht FILE* file? -1
Herr Oscar
-1, weil Dateifunktionen Dateideskriptoren und keine Dateipfade akzeptieren sollten
Herr Oscar

Antworten:

144

Basierend auf dem Code von 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; 
}

Änderungen:

  • Das Dateinamenargument wurde a const char.
  • Die struct statDefinition wurde korrigiert , bei der der Variablenname fehlte.
  • Gibt -1bei Fehler statt zurück 0, was für eine leere Datei nicht eindeutig wäre. off_tist ein signierter Typ, daher ist dies möglich.

Wenn Sie fsize()eine Fehlermeldung drucken möchten , können Sie Folgendes verwenden:

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

Auf 32-Bit-Systemen sollten Sie dies mit der Option kompilieren -D_FILE_OFFSET_BITS=64, da sonst off_tnur Werte bis zu 2 GB gespeichert werden. Weitere Informationen finden Sie im Abschnitt "Verwenden von LFS" der Unterstützung großer Dateien unter Linux .

Ted Percival
quelle
19
Dies ist Linux / Unix-spezifisch - wahrscheinlich erwähnenswert, da in der Frage kein Betriebssystem angegeben wurde.
Drew Hall
1
Sie könnten wahrscheinlich den Rückgabetyp in ssize_t ändern und die Größe ohne Probleme von off_t umwandeln. Es scheint sinnvoller zu sein, ein ssize_t zu verwenden :-) (Nicht zu verwechseln mit size_t, das nicht signiert ist und nicht zur Anzeige eines Fehlers verwendet werden kann.)
Ted Percival
1
Verwenden Sie für portableren Code fseek+, ftellwie von Derek vorgeschlagen.
Ciro Santilli 法轮功 冠状 病 六四 事件 2
9
Verwenden Sie für portableren Code fseek+, ftellwie von Derek vorgeschlagen. Nein. Der C-Standard besagt ausdrücklich, dass fseek()to SEEK_ENDin einer Binärdatei ein undefiniertes Verhalten ist. 7.19.9.2 Die fseekFunktion ... Ein binärer Stream muss fseekAufrufe mit einem Woher-Wert vonSEEK_END und, wie unten angegeben, aus Fußnote 234 auf S. 22 nicht sinnvoll unterstützen . 267 des C - Standard verbunden ist , und welche spezifisch Etiketten fseekzu SEEK_ENDin einem Binärstrom als undefiniertes Verhalten. .
Andrew Henle
74

Nicht benutzen int . Dateien mit einer Größe von mehr als 2 Gigabyte sind heutzutage als Schmutz üblich

Nicht benutzen unsigned int . Dateien mit einer Größe von mehr als 4 Gigabyte sind häufig als etwas weniger häufig vorkommender Schmutz

IIRC Die Standardbibliothek definiert off_tals vorzeichenlose 64-Bit-Ganzzahl, die jeder verwenden sollte. Wir können das in ein paar Jahren auf 128 Bit neu definieren, wenn 16 Exabyte-Dateien herumhängen.

Wenn Sie unter Windows arbeiten, sollten Sie GetFileSizeEx verwenden - es wird tatsächlich eine vorzeichenbehaftete 64-Bit-Ganzzahl verwendet, sodass Probleme mit 8 Exabyte-Dateien auftreten. Dummes Microsoft! :-)

Orion Edwards
quelle
1
Ich habe Compiler verwendet, bei denen off_t 32 Bit beträgt. Zugegeben, dies ist auf eingebetteten Systemen der Fall, auf denen 4-GB-Dateien weniger häufig sind. Wie auch immer, POSIX definiert auch off64_t und entsprechende Methoden, um die Verwirrung zu vergrößern.
Aaron Campbell
Ich liebe immer Antworten, die Windows voraussetzen und nichts anderes tun, als die Frage zu kritisieren. Könnten Sie bitte etwas hinzufügen, das POSIX-konform ist?
SS Anne
1
@ JL2210 Die akzeptierte Antwort von Ted Percival zeigt eine Posix-konforme Lösung, daher sehe ich keinen Sinn darin, das Offensichtliche zu wiederholen. Ich (und 70 andere) waren der Meinung, dass das Hinzufügen des Hinweises zu Windows und das Nichtverwenden von vorzeichenbehafteten 32-Bit-Ganzzahlen zur Darstellung von Dateigrößen ein Mehrwert ist. Prost
Orion Edwards
30

Matts Lösung sollte funktionieren, außer dass es C ++ anstelle von C ist und das anfängliche Tell nicht notwendig sein sollte.

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

Reparierte auch deine Zahnspange für dich. ;)

Update: Dies ist nicht wirklich die beste Lösung. Es ist auf 4 GB-Dateien unter Windows beschränkt und wahrscheinlich langsamer als nur ein plattformspezifischer Aufruf wie GetFileSizeExoder stat64.

Derek Park
quelle
Ja du solltest. Sofern es keinen wirklich zwingenden Grund gibt, nicht plattformspezifisch zu schreiben, sollten Sie jedoch wahrscheinlich nur einen plattformspezifischen Aufruf anstelle des Musters open / seek-end / tell / close verwenden.
Derek Park
1
Entschuldigung für die späte Antwort, aber ich habe hier ein großes Problem. Die App bleibt hängen, wenn auf eingeschränkte Dateien zugegriffen wird (z. B. kennwortgeschützte Dateien oder Systemdateien). Gibt es eine Möglichkeit, den Benutzer bei Bedarf nach einem Passwort zu fragen?
Justin
@Justin, Sie sollten wahrscheinlich eine neue Frage speziell zu dem Problem, auf das Sie stoßen, öffnen und Details zu der Plattform, auf der Sie sich befinden, wie Sie auf die Dateien zugreifen und wie sich das Verhalten verhält.
Derek Park
1
Sowohl C99 als auch C11 kehren long intvon zurück ftell(). (unsigned long)Das Gießen verbessert die Reichweite nicht, da sie bereits durch die Funktion begrenzt ist. ftell()Rückgabe -1 bei Fehler und das wird mit der Besetzung verschleiert. Schlagen Sie vor fsize(), den gleichen Typ wie zurückzugeben ftell().
chux
Genau. Die Besetzung sollte dem ursprünglichen Prototyp in der Frage entsprechen. Ich kann mich nicht erinnern, warum ich es in unsigned long anstatt in unsigned int verwandelt habe.
Derek Park
15

** Tu das nicht ( warum? ):

Zitieren des C99-Standarddokuments, das ich online gefunden habe: "Das Setzen des Dateipositionsindikators auf Dateiende hat wie bei ein fseek(file, 0, SEEK_END)undefiniertes Verhalten für einen Binärdatenstrom (aufgrund möglicher nachfolgender Nullzeichen) oder für jeden Datenstrom mit zustandsabhängiger Codierung das endet nicht sicher im anfänglichen Schaltzustand. **

Ändern Sie die Definition in int, damit Fehlermeldungen übertragen werden können, und verwenden Sie dann fseek()und ftell(), um die Dateigröße zu bestimmen.

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
quelle
5
@mezhaka: Dieser CERT-Bericht ist einfach falsch. fseekound ftello(oder fseekund ftellwenn Sie nicht weiterkommen , ohne die ehemaligen und glücklich mit Grenzen für die Dateigrößen können Sie mit der Arbeit) sind der richtige Weg , um die Länge einer Datei zu bestimmen. stat-basierte Lösungen funktionieren nicht auf vielen "Dateien" (z. B. Blockgeräten) und sind nicht auf nicht POSIX-ähnliche Systeme portierbar.
R .. GitHub STOP HELPING ICE
1
Dies ist die einzige Möglichkeit, die Dateigröße auf vielen nicht posix-kompatiblen Systemen (wie meinem sehr minimalistischen mbed) zu ermitteln
Earlz
9

POSIX

Der POSIX- Standard verfügt über eine eigene Methode zum Abrufen der Dateigröße.
Fügen Sie den sys/stat.hHeader ein, um die Funktion zu verwenden.

Zusammenfassung

  • Holen Sie sich Dateistatistiken mit stat(3).
  • Erhalten Sie die st_sizeEigenschaft.

Beispiele

Hinweis : Die Größe wird auf begrenzt 4GB. Wenn nicht Fat32Dateisystem, dann verwenden Sie die 64-Bit-Version!

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

Das ANSI C bietet nicht direkt die Möglichkeit, die Länge der Datei zu bestimmen.
Wir müssen unseren Verstand benutzen. Im Moment verwenden wir den Suchansatz!

Zusammenfassung

  • Suchen Sie die Datei bis zum Ende mit fseek(3) .
  • Holen Sie sich die aktuelle Position mit ftell(3).

Beispiel

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

Wenn die Datei stdinoder eine Pipe ist. POSIX, ANSI C funktioniert nicht.
Es wird zurückgegeben, 0wenn die Datei eine Pipe ist oder stdin.

Meinung : Sie sollten stattdessen den POSIX- Standard verwenden. Weil es 64-Bit-Unterstützung hat.


quelle
1
struct _stat64und __stat64()für _Windows.
Bob Stein
5

Und wenn Sie eine Windows-App erstellen , verwenden Sie die GetFileSizeEx- API, da die CRT-Datei-E / A aufgrund von Besonderheiten bei der Dateidarstellung auf verschiedenen Systemen unübersichtlich ist, insbesondere zum Bestimmen der Dateilänge.


quelle
5

Wenn Sie mit der Verwendung der Standard-C-Bibliothek einverstanden sind:

#include <sys/stat.h>
off_t fsize(char *file) {
    struct stat filestat;
    if (stat(file, &filestat) == 0) {
        return filestat.st_size;
    }
    return 0;
}
NilObject
quelle
24
Das ist nicht Standard C. Es ist Teil des POSIX-Standards, aber nicht des C-Standards.
Derek Park
3

Bei einer schnellen Suche in Google wurde eine Methode mit fseek und ftell gefunden sowie einen Thread mit dieser Frage mit Antworten, die auf andere Weise nicht nur in C ausgeführt werden können.

Sie können eine Portabilitätsbibliothek wie NSPR (die Bibliothek, die Firefox unterstützt) verwenden oder deren Implementierung überprüfen (ziemlich haarig).

Nickolay
quelle
1

Ich habe diesen Code verwendet, um die Dateilänge zu ermitteln.

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

Versuche dies --

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

Suchen Sie zunächst bis zum Ende der Datei. Geben Sie dann an, wo sich der Dateizeiger befindet. Zuletzt (dies ist optional) wird zum Anfang der Datei zurückgespult. Beachten Sie, dassfp dies ein binärer Stream sein sollte.

file_size enthält die Anzahl der Bytes, die die Datei enthält. Da der vorzeichenlose lange Typ (laut klimits.h) auf 4294967295 Byte (4 Gigabyte) begrenzt ist, müssen Sie einen anderen Variablentyp finden, wenn Sie wahrscheinlich mit größeren Dateien arbeiten.

Adrian
quelle
3
Wie unterscheidet sich das von Dereks Antwort von vor 8 Jahren?
PP
Dies ist ein undefiniertes Verhalten für einen Binärdatenstrom, und für einen Textdatenstrom ftellwird kein Wert zurückgegeben, der für die Anzahl der Bytes repräsentativ ist, die aus der Datei gelesen werden können.
Andrew Henle
0

Ich habe eine Funktion, die nur gut funktioniert stdio.h. Ich mag es sehr und es funktioniert sehr gut und ist ziemlich prägnant:

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

Hier ist eine einfache und saubere Funktion, die die Dateigröße zurückgibt.

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 
}
Abdessamad Doughri
quelle
1
Müssen Sie die Datei nicht schließen?
Jerry Jeremiah
Nein, ich mag keine Funktionen, die einen Pfad erwarten. Bitte lassen Sie stattdessen einen Dateizeiger erwarten
Mr Oscar
-3

Sie können die Datei öffnen und mit dem Versatz 0 vom unteren Rand der Datei auf 0 gehen

#define SEEKBOTTOM   2

fseek(handle, 0, SEEKBOTTOM)  

Der von fseek zurückgegebene Wert entspricht der Größe der Datei.

Ich habe lange nicht in C codiert, aber ich denke, es sollte funktionieren.

PabloG
quelle
12
Sie sollten nicht so etwas wie SEEKBOTTOM definieren müssen. #include <stdio.h> fseek (handle, 0, SEEK_END);
Sigjuice
-4

Wenn Sie sich die Frage ansehen, ftellkönnen Sie leicht die Anzahl der Bytes ermitteln.

  long size = ftell(FILENAME);
  printf("total size is %ld bytes",size);
Zishan
quelle
ftellerwartet als Argument einen Dateideskriptor, keinen Dateinamen.
Barmar
@Barmar, Nein ftellerwartet keinen Dateideskriptor, sondern einen FILE*. Siehe zuerst die Manpage!
Der Ansatz ist völlig falsch. Es ist eine Konstante, ftelldie 0jedes Mal zurückkehren würde!
Diese Antwort ist völlig falsch, da Sie zum einen fseek()zuerst das Ende der Datei suchen müssen und außerdem eine Zeichenfolge ftell()erwarten FILE *, keine Zeichenfolge! Es wäre gut, wenn Sie Ihre Antwort konkretisieren würden.
Herr Oscar