583 Stimmen

Wie kann ich am besten überprüfen, ob eine Datei in C existiert?

Gibt es einen besseren Weg, als einfach zu versuchen, die Datei zu öffnen?

int exists(const char *fname)
{
    FILE *file;
    if ((file = fopen(fname, "r")))
    {
        fclose(file);
        return 1;
    }
    return 0;
}

805voto

Graeme Perrow Punkte 53901

Schauen Sie sich die access() Funktion, zu finden in unistd.h . Sie können Ihre Funktion ersetzen durch

if( access( fname, F_OK ) == 0 ) {
    // file exists
} else {
    // file doesn't exist
}

Unter Windows (VC) unistd.h gibt es nicht. Damit es funktioniert, ist es notwendig, zu definieren:

#ifdef WIN32
#include <io.h>
#define F_OK 0
#define access _access
#endif

Sie können auch Folgendes verwenden R_OK , W_OK et X_OK anstelle von F_OK um die Lese-, Schreib- bzw. Ausführungsberechtigung zu prüfen, anstatt die Existenz zu prüfen, und Sie können eine beliebige ODER-Verknüpfung vornehmen (d. h. sowohl die Lese- als auch die Schreibberechtigung prüfen). et Schreibrecht mit R_OK|W_OK )

Update : Beachten Sie, dass Sie unter Windows nicht die Funktion W_OK um zuverlässig auf eine Schreibberechtigung zu testen, da die Zugriffsfunktion keine DACLs berücksichtigt. access( fname, W_OK ) kann 0 (Erfolg) zurückgeben, weil die Datei nicht das Nur-Lese-Attribut gesetzt hat, aber Sie haben möglicherweise immer noch keine Berechtigung, in die Datei zu schreiben.

2 Stimmen

Bei mir funktioniert es nicht, egal ob die Datei existiert oder nicht, es wird -1 zurückgegeben.

3 Stimmen

@JonathanLeffler Während Ihre Kommentare wahr sind, sollte access() für die Überprüfung in Ordnung sein Existenz einer Datei ( F_OK ).

14 Stimmen

Meistens ja (es ist OK, die access() um das Vorhandensein einer Datei zu prüfen), aber in einem SUID- oder SGID-Programm könnte selbst das falsch sein. Wenn sich die geprüfte Datei in einem Verzeichnis befindet, auf das die echte UID oder die echte GID nicht zugreifen kann, access() meldet möglicherweise keine solche Datei, obwohl sie existiert. Esoterisch und unwahrscheinlich? Ja.

151voto

codebunny Punkte 2679

使用方法 stat wie diese:

#include <sys/stat.h>   // stat
#include <stdbool.h>    // bool type

bool file_exists (char *filename) {
  struct stat   buffer;   
  return (stat (filename, &buffer) == 0);
}

und nennen Sie es so:

#include <stdio.h>      // printf

int main(int ac, char **av) {
    if (ac != 2)
        return 1;

    if (file_exists(av[1]))
        printf("%s exists\n", av[1]);
    else
        printf("%s does not exist\n", av[1]);

    return 0;
}

5 Stimmen

@LudvigANorin: Auf solchen Systemen ist die Wahrscheinlichkeit groß, dass access() hat auch Probleme, und es gibt Optionen, mit denen sich die access() y stat() Arbeit mit großen Dateien (größer als 2 GB).

20 Stimmen

Kann einer von Ihnen eine Dokumentation über den Ausfall nach 2 GB vorlegen? Und was ist die Alternative in solchen Fällen?

0 Stimmen

@JonathanLeffler Does stat nicht unter der gleichen TOCTOU-Schwachstelle leiden wie access ? (Es ist mir nicht klar, dass es besser wäre.)

103voto

Dan Lenski Punkte 72055

Wenn Sie prüfen wollen, ob eine Datei existiert, dann wollen Sie normalerweise erstellen. diese Datei, wenn sie nicht vorhanden ist. Graeme Perrow's Antwort ist gut, wenn Sie nicht diese Datei erstellen wollen, aber es ist anfällig für eine Race Condition, wenn Sie das tun: ein anderer Prozess könnte die Datei erstellen, während Sie prüfen, ob sie existiert, und sie tatsächlich öffnen, um in sie zu schreiben. (Lachen Sie nicht... das könnte schlecht Auswirkungen auf die Sicherheit, wenn die erstellte Datei ein Symlink war!)

Wenn Sie die Existenz prüfen wollen et die Datei erstellen, wenn sie nicht existiert, atomar so dass es keine Wettlaufbedingungen gibt, dann verwenden Sie dies:

#include <fcntl.h>
#include <errno.h>

fd = open(pathname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
if (fd < 0) {
  /* failure */
  if (errno == EEXIST) {
    /* the file already existed */
    ...
  }
} else {
  /* now you can use the file */
}

9 Stimmen

Wenn Sie O_CREAT verwenden wollen, müssen Sie den Modus (Berechtigungen) als drittes Argument an open() übergeben. Überlegen Sie auch, ob O_TRUNC oder O_EXCL oder O_APPEND verwendet werden soll.

6 Stimmen

Jonathan Leffler hat recht, dieses Beispiel erfordert O_EXCL, um wie geschrieben zu funktionieren.

6 Stimmen

Außerdem müssen Sie den Modus als drittes Argument angeben: open(lock, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR)

36voto

Mecki Punkte 113876

Ja. Verwenden Sie . stat() . Siehe die Manpage für stat(2) .

stat() wird fehlschlagen, wenn die Datei nicht existiert, ansonsten wird sie wahrscheinlich erfolgreich sein. Wenn die Datei zwar existiert, Sie aber keinen Lesezugriff auf das Verzeichnis haben, in dem sie sich befindet, schlägt sie ebenfalls fehl, aber in diesem Fall schlägt jede Methode fehl (wie können Sie den Inhalt eines Verzeichnisses untersuchen, das Sie aufgrund von Zugriffsrechten nicht sehen dürfen? Ganz einfach, man kann es nicht).

Oh, wie schon jemand anderes erwähnt hat, können Sie auch access() . Ich bevorzuge jedoch stat() Denn wenn die Datei existiert, erhalte ich sofort viele nützliche Informationen (wann wurde sie zuletzt aktualisiert, wie groß ist sie, welcher Eigentümer und/oder welche Gruppe ist Eigentümer der Datei, welche Zugriffsrechte hat sie usw.).

10 Stimmen

Zugriff ist vorzuziehen, wenn Sie nur wissen müssen, ob die Datei existiert. Stat() kann einen großen Overheard haben, wenn Sie nicht alle zusätzlichen Informationen benötigen.

4 Stimmen

Wenn ich ein Verzeichnis mit dem ls-Befehl aufliste, ruft es stat für jede Datei auf, die dort vorhanden ist, und dass die Ausführung von ls einen großen Overhead hat, ist mir ziemlich neu. Tatsächlich kann man ls auf Verzeichnisse mit Tausenden von Dateien anwenden und es kommt in einem Bruchteil einer Sekunde zurück.

3 Stimmen

@Mecki: stat hat im Vergleich zum Zugriff auf Systeme, die Hardlinks unterstützen, einen zusätzlichen Overhead von nicht Null. Das liegt daran, dass access sich nur den Verzeichniseintrag ansehen muss, während stat auch den Inode nachschlagen muss. Auf Speichergeräten mit schlechter Suchzeit (z.B. Band) kann der Unterschied erheblich sein, da der Verzeichniseintrag und der Inode wahrscheinlich nicht nebeneinander liegen.

21voto

mesutpiskin Punkte 1450
FILE *file;
    if((file = fopen("sample.txt","r"))!=NULL)
        {
            // file exists
            fclose(file);
        }
    else
        {
            //File not found, no memory leak since 'file' == NULL
            //fclose(file) would cause an error
        }

28 Stimmen

fopen() ist Standard-C, es wird nicht verschwinden. Es ist nur von Microsoft "veraltet". Verwenden Sie nicht fopen_s() es sei denn, Sie wollen plattformspezifischen, nicht portierbaren Code.

0 Stimmen

Dies ist die kompatibelste für alle alten und neuen Systeme.

0 Stimmen

Wo befindet sich der boolesche Satz?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X