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?

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.


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();

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

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"

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
}

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.


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.


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

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

Bitte verwenden Sie basename / dirnameKonstrukte.

Mann Basisname


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.


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)


5
Ich glaube, dies ist eine Delphi-Methode, nicht etwas in C ++.
Ian Hunter
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.