Abrufen eines Verzeichnisnamens aus einem Dateinamen

85

Ich habe einen Dateinamen (C: \ Ordner \ foo.txt) und muss den Ordnernamen (C: \ Ordner) in nicht verwaltetem C ++ abrufen. In C # würde ich so etwas machen:

string folder = new FileInfo("C:\folder\foo.txt").DirectoryName;

Gibt es eine Funktion, die in nicht verwaltetem C ++ verwendet werden kann, um den Pfad aus dem Dateinamen zu extrahieren?

Jon Tackabury
quelle

Antworten:

20

Hierfür gibt es eine Standard-Windows-Funktion, PathRemoveFileSpec . Wenn Sie nur Windows 8 und höher unterstützen, wird dringend empfohlen, stattdessen PathCchRemoveFileSpec zu verwenden. Unter anderem ist es nicht mehr auf MAX_PATH(260) Zeichen beschränkt.

Andreas Rejbrand
quelle
2
Beachten Sie, dass diese Funktion jetzt veraltet ist. Der Vorschlag von Microsoft lautet, stattdessen PathCchRemoveFileSpec zu verwenden.
Standard
1
@Default: PathCchRemoveFileSpec ist nur ab Windows 8 verfügbar. Da Windows Vista und 7 weiterhin unterstützt werden, gilt dies auch für PathRemoveFileSpec .
IInspectable
153

Verwenden von Boost.Filesystem:

boost::filesystem::path p("C:\\folder\\foo.txt");
boost::filesystem::path dir = p.parent_path();
AraK
quelle
2
p.remove_filename()wird pan Ort und Stelle geändert und kann effizienter implementiert werden alsp = p.parent_path()
Peter Cordes
Wenn Sie sich auch mit Verzeichnissen befassen, sollten Sie sich der Tatsache bewusst sein, dass parent_path()daraus "C:\\folder"resultiert "C:".
Semjon Mössinger
Viele Boosts werden auf std aktualisiert. Versuchen Sie dies auch .... include <Dateisystem> .... std :: experimentell :: Dateisystem :: Pfad p ("C: \\ Ordner \\ foo.txt");
S Meaden
72

Beispiel von http://www.cplusplus.com/reference/string/string/find_last_of/

// string::find_last_of
#include <iostream>
#include <string>
using namespace std;

void SplitFilename (const string& str)
{
  size_t found;
  cout << "Splitting: " << str << endl;
  found=str.find_last_of("/\\");
  cout << " folder: " << str.substr(0,found) << endl;
  cout << " file: " << str.substr(found+1) << endl;
}

int main ()
{
  string str1 ("/usr/bin/man");
  string str2 ("c:\\windows\\winhelp.exe");

  SplitFilename (str1);
  SplitFilename (str2);

  return 0;
}
corsiKa
quelle
1
Dies ist hier die beste Minimallösung.
plasmacel
38

In C ++ 17 existiert eine Klasse std::filesystem::path, die die Methode verwendet parent_path.

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
    for(fs::path p : {"/var/tmp/example.txt", "/", "/var/tmp/."})
        std::cout << "The parent path of " << p
                  << " is " << p.parent_path() << '\n';
}

Mögliche Ausgabe:

The parent path of "/var/tmp/example.txt" is "/var/tmp"
The parent path of "/" is ""
The parent path of "/var/tmp/." is "/var/tmp"
Alessandro Jacopson
quelle
2
Es gibt auch eine .remove_filename()Methode.
Qqwy
1
Vielen Dank, @Qqwy, es erlaubt auch, den Verzeichnispfad mit dieser Methode zu verwenden, um korrekte und erwartete Ergebnisse zu erhalten, im Gegensatz zu dem Ansatz aus der Antwort
Herrgott
13

Warum muss es so kompliziert sein?

#include <windows.h>

int main(int argc, char** argv)         // argv[0] = C:\dev\test.exe
{
    char *p = strrchr(argv[0], '\\');
    if(p) p[0] = 0;

    printf(argv[0]);                    // argv[0] = C:\dev
}
toster-cx
quelle
10
Dies ist nicht tragbar. Das Pfadtrennzeichen unter Linux ist '/'. std :: filesystem :: path ist Standard und portabel.
Rémi
7
 auto p = boost::filesystem::path("test/folder/file.txt");
 std::cout << p.parent_path() << '\n';             // test/folder
 std::cout << p.parent_path().filename() << '\n';  // folder
 std::cout << p.filename() << '\n';                // file.txt

Möglicherweise müssen p.parent_path().filename()Sie den Namen des übergeordneten Ordners abrufen.

srbcheema1
quelle
5

Verwenden Sie boost :: filesystem. Es wird sowieso in den nächsten Standard aufgenommen, so dass Sie sich genauso gut daran gewöhnen können.

Edward Strange
quelle
1
Über welchen Standard sprichst du? Ich weiß, dass viele Dinge von Boost zu C ++ std lib hinzugefügt wurden, Dateisystem wird auch hinzugefügt?
McLeary
7
"Es wird sowieso in den nächsten Standard aufgenommen" Und es ist nicht
Anton K
@AntonK vielleicht C ++ 2017?
Alessandro Jacopson
6
@AlessandroJacopson Cool, es scheint in C ++ 17 enthalten zu sein - en.cppreference.com/w/cpp/filesystem
Anton K
1

_splitpath ist eine schöne CRT-Lösung.

Ofek Shilon
quelle
1

Ich bin so überrascht, dass niemand den Standardweg in Posix erwähnt hat

Bitte verwenden Sie basename / dirnameKonstrukte.

Mann Basisname

Utkarsh Kumar
quelle
Die POSIX-Funktionen sind nicht ohne Nachteile. Insbesondere können sie den Puffer ändern, den Sie übergeben (sie bedeuten wirklich, dass die Signatur ist basname(char * path)und nicht basename(const char * path)), und Implementierungen, die dies nicht tun, müssen anscheinend einen statischen Puffer verwenden, der sie threadsicher macht (im Prinzip Sie könnte auch dynamisch zugewiesene Ergebnisse zurückgeben, aber das macht Sie abhängig von allocFamilienfunktionen, was in C ++ umständlich ist.
dmckee --- Ex-Moderator Kätzchen
-1

Standard C ++ wird in dieser Hinsicht nicht viel für Sie tun, da Pfadnamen plattformspezifisch sind. Sie können die Zeichenfolge manuell analysieren (wie in der Antwort von Glowcoder), Betriebssystemfunktionen verwenden (z. B. http://msdn.microsoft.com/en-us/library/aa364232(v=VS.85).aspx ) oder wahrscheinlich die Am besten können Sie eine Dateisystembibliothek eines Drittanbieters wie boost :: filesystem verwenden.

Zahnrad
quelle
Das C ++ 1z des Standards versucht derzeit, die Boost-Dateisystembibliothek zu übernehmen, sodass die Plattformfreundlichkeit jetzt weniger ein Problem darstellt. Zumindest in den experimentellen Headern für MSVC.
KayleeFrye_onDeck
-6

Verwenden Sie einfach Folgendes: ExtractFilePath (Ihr_Pfad_Dateiname)

fxPiotr
quelle
5
Ich glaube, dies ist eine Delphi-Methode, nicht etwas in C ++.
Ian Hunter