642 Stimmen

Der schnellste Weg, um zu prüfen, ob eine Datei mit Standard-C++/C++11,14,17/C existiert?

Ich möchte den schnellsten Weg finden, um zu prüfen, ob eine Datei in Standard C++11, 14, 17 oder C existiert. Ich habe Tausende von Dateien und bevor ich etwas mit ihnen mache, muss ich prüfen, ob sie alle existieren. Was kann ich anstelle von /* SOMETHING */ in der folgenden Funktion?

inline bool exist(const std::string& name)
{
    /* SOMETHING */
}

7voto

ravin.wang Punkte 1092

Weitere 3 Optionen unter Windows:

1

inline bool exist(const std::string& name)
{
    OFSTRUCT of_struct;
    return OpenFile(name.c_str(), &of_struct, OF_EXIST) != INVALID_HANDLE_VALUE && of_struct.nErrCode == 0;
}

2

inline bool exist(const std::string& name)
{
    HANDLE hFile = CreateFile(name.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != NULL && hFile != INVALID_HANDLE)
    {
         CloseFile(hFile);
         return true;
    }
    return false;
}

3

inline bool exist(const std::string& name)
{
    return GetFileAttributes(name.c_str()) != INVALID_FILE_ATTRIBUTES;
}

6voto

Valid Punkte 707

Wenn Sie zwischen einer Datei und einem Verzeichnis unterscheiden müssen, sollten Sie die folgenden Möglichkeiten in Betracht ziehen, die beide stat verwenden, das schnellste Standardwerkzeug, wie von PherricOxide demonstriert:

#include <sys/stat.h>
int FileExists(char *path)
{
    struct stat fileStat; 
    if ( stat(path, &fileStat) )
    {
        return 0;
    }
    if ( !S_ISREG(fileStat.st_mode) )
    {
        return 0;
    }
    return 1;
}

int DirExists(char *path)
{
    struct stat fileStat;
    if ( stat(path, &fileStat) )
    {
        return 0;
    }
    if ( !S_ISDIR(fileStat.st_mode) )
    {
        return 0;
    }
    return 1;
}

5voto

Jayhello Punkte 4867

Sie können verwenden std::ifstream , Funktionen wie is_open , fail z.B. als untenstehender Code (der cout "open" bedeutet, dass die Datei existiert oder nicht):

enter image description here

enter image description here

zitiert von diesem Antwort

4voto

jack chyna Punkte 21

Nun, es gibt einen noch einfacheren Weg

#include <fstream>
#include <iostream>

void FileExists(std::string myfile){
std::ifstream file(myfile.c_str());

if (file) {
    std::cout << "file exists" << std::endl;
}
else {
    std::cout << "file doesn't exist" << std::endl;
}
}

int main() {
FileExists("myfile.txt");

return 0;
}

4voto

mtraceur Punkte 2354

Alle anderen Antworten konzentrieren sich darauf, jede Datei einzeln zu überprüfen, aber wenn sich die Dateien alle in einem Verzeichnis (Ordner) befinden, könnte es viel effizienter sein, nur das Verzeichnis lesen und prüfen Sie, ob alle gewünschten Dateinamen vorhanden sind.

Dies kann sogar effizienter sein, wenn die Dateien über mehrere Verzeichnisse verteilt sind, je nach dem genauen Verhältnis von Verzeichnissen zu Dateien. Sobald sich jede Zieldatei in einem eigenen Verzeichnis befindet oder sich viele andere Dateien in denselben Verzeichnissen befinden, die nicht überprüft werden sollen, würde ich erwarten, dass es letztendlich weniger effizient ist als jede Datei einzeln zu überprüfen.

Eine gute Heuristik: Es ist viel schneller, mit einer Menge von Daten zu arbeiten, die man bereits hat, als das Betriebssystem nach einer beliebigen Menge von Daten zu fragen. Der Overhead bei Systemaufrufen ist im Vergleich zu einzelnen Maschinenbefehlen enorm. Es ist also fast immer schneller, das Betriebssystem zu fragen: "Gib mir die gesamte Liste der Dateien in diesem Verzeichnis" und sich dann durch diese Liste zu wühlen, und langsamer, das Betriebssystem zu fragen: "Gib mir Informationen über diese Datei", "Okay, jetzt gib mir Informationen über diese andere Datei", "Jetzt gib mir Informationen über ...", und so weiter.

Jede gute C-Bibliothek implementiert ihre "Iteration über alle Dateien in einem Verzeichnis"-APIs auf effiziente Weise, genau wie gepufferte E/A - intern liest sie eine große Liste von Verzeichniseinträgen vom Betriebssystem auf einmal, auch wenn die APIs so aussehen, als würde man das Betriebssystem nach jedem Eintrag einzeln fragen.


Wenn ich also diese Anforderung hätte, würde ich

  1. alles zu tun, um die Gestaltung und Nutzung so zu fördern, dass sich alle Dateien in einem Ordner befinden und keine anderen Dateien in diesem Ordner sind,
  2. die Liste der Dateinamen, die ich benötige, in einer Datenstruktur im Speicher ablegen, die O(1) oder zumindest O(log(n)) Such- und Löschzeiten hat (wie eine Hash-Map oder ein Binärbaum),
  3. die Dateien in diesem Verzeichnis auflisten und nach und nach aus der "Liste" (Hash-Map oder Binärbaum) im Speicher "abhaken" (löschen).

Außer je nach dem genauen Anwendungsfall würde ich vielleicht statt des Löschens von Einträgen aus einer Hash-Map oder einem Baum ein "Habe ich diese Datei?"-Boolesches Ergebnis für jeden Eintrag verfolgen und eine Datenstruktur finden, die die Frage "Habe ich jede Datei?" O(1) macht. Vielleicht ein Binärbaum, aber die Struktur für jeden Nicht-Blattknoten hat auch einen Booleschen Wert, der ein logisches Und der Booleschen Werte seiner Blattknoten ist. Das skaliert gut - nachdem man einen Booleschen Wert in einem Blattknoten gesetzt hat, geht man einfach den Baum hinauf und setzt den Booleschen Wert jedes Knotens "habe ich das?" mit der && des booleschen Wertes des untergeordneten Knotens (und Sie brauchen nicht auf die anderen untergeordneten Knoten zu rekursieren, denn wenn Sie diesen Prozess jedes Mal konsequent durchführen, wenn Sie eines der Blätter auf true setzen, werden sie nur dann auf true gesetzt, wenn alle ihre untergeordneten Knoten true sind).


Leider gibt es keine Standard bis C++17 zu tun.

C++17 hat std::filesystem::directory_iterator .

Natürlich gibt es eine entsprechende boost::filesystem::directory_iterator was vermutlich auch in älteren Versionen von C++ funktioniert.

Das, was einem Standard-C-Weg am nächsten kommt, ist opendir y readdir von dirent.h . Das ist eine Standard-C-Schnittstelle, sie ist nur in POSIX standardisiert und nicht im C-Standard selbst. Sie ist auf Mac OS, Linux, allen BSDs, anderen UNIX/UNIX-ähnlichen Systemen und jedem anderen POSIX/SUS-System sofort verfügbar. Für Windows gibt es eine dirent.h Umsetzung die Sie einfach herunterladen und in Ihrem Include-Pfad ablegen müssen.

Da Sie jedoch nach dem schnellste Vielleicht sollten Sie sich nicht nur mit den tragbaren/standardmäßigen Produkten befassen.

Unter Linux können Sie die Leistung möglicherweise optimieren, indem Sie die Puffergröße manuell mit dem Raw-Systemaufruf angeben getdents64 .

Unter Windows, nach ein wenig Suchen, es sieht so aus Für eine maximale Leistung sollten Sie Folgendes verwenden FindFirstFileEx con FindExInfoBasic y FIND_FIRST_EX_LARGE_FETCH wenn Sie können, was viele der Open-Source-Bibliotheken wie die oben genannte dirent.h für Windows scheinen nicht zu funktionieren. Aber für Code, der mit Dingen arbeiten muss, die älter sind als die letzten paar Windows-Versionen, können Sie genauso gut einfach die unkomplizierte FindFirstFile ohne die zusätzlichen Flaggen.

Der Plan 9 wird von keinem der oben genannten Pläne abgedeckt, und Sie benötigen dort dirread o dirreadall (letzteres, wenn Sie sicher davon ausgehen können, dass Sie genügend Speicher für den gesamten Verzeichnisinhalt haben). Wenn Sie aus Leistungsgründen mehr Kontrolle über die Puffergröße haben wollen, verwenden Sie einfach read o read und dekodieren die Daten des Verzeichniseintrags - sie liegen in einem dokumentierten, maschinenunabhängigen Format vor, und ich glaube, es werden Hilfsfunktionen angeboten.

Ich weiß nichts über andere Betriebssysteme.


Vielleicht bearbeite ich diese Antwort später mit einigen Tests. Andere können auch gerne Testergebnisse einfügen.

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