Pobieranie nazwy katalogu z nazwy pliku

85

Mam nazwę pliku (C: \ folder \ foo.txt) i muszę pobrać nazwę folderu (C: \ folder) w niezarządzanym C ++. W C # zrobiłbym coś takiego:

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

Czy istnieje funkcja, której można użyć w niezarządzanym C ++ do wyodrębnienia ścieżki z nazwy pliku?

Jon Tackabury
źródło

Odpowiedzi:

20

Jest do tego standardowa funkcja Windows, PathRemoveFileSpec . Jeśli obsługujesz tylko system Windows 8 i nowsze, zdecydowanie zaleca się użycie zamiast tego PathCchRemoveFileSpec . Oprócz innych ulepszeń, nie jest już ograniczony do MAX_PATH(260) znaków.

Andreas Rejbrand
źródło
2
Zauważ, że ta funkcja jest obecnie przestarzała. Sugestią firmy Microsoft jest użycie zamiast tego PathCchRemoveFileSpec .
Domyślnie
1
@Default: PathCchRemoveFileSpec jest dostępne tylko począwszy od systemu Windows 8. Ponieważ systemy Windows Vista i 7 są nadal obsługiwane, tak samo jest z parametrami PathRemoveFileSpec .
Niespodziewane
153

Korzystanie z Boost.Filesystem:

boost::filesystem::path p("C:\\folder\\foo.txt");
boost::filesystem::path dir = p.parent_path();
AraK
źródło
2
p.remove_filename()zmodyfikuje pna miejscu i może być wdrożony wydajniej niżp = p.parent_path()
Peter Cordes,
Jeśli możesz również zajmować się katalogami, pamiętaj, że parent_path()from "C:\\folder"spowoduje rozszerzenie "C:".
Semjon Mössinger
wiele ulepszeń zostanie zaktualizowanych do standardowego, więc spróbuj też tego .... dołącz <system plików> .... std :: eksperyment :: system plików :: ścieżka p ("C: \\ folder \\ foo.txt");
S Meaden
72

Przykład z 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
źródło
1
To najlepsze minimalne rozwiązanie tutaj.
plasmacel
39

W C ++ 17 istnieje klasa std::filesystem::pathkorzystająca z metody 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';
}

Możliwe wyjście:

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
źródło
2
Istnieje również .remove_filename()metoda.
Qqwy
1
Dzięki @Qqwy, pozwala również na użycie ścieżki katalogu z tą metodą, aby uzyskać poprawne i oczekiwane wyniki w przeciwieństwie do podejścia z odpowiedzi
Herrgott
13

Dlaczego to musi być takie skomplikowane?

#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
źródło
10
To nie jest przenośne. Separatorem ścieżek w Linuksie jest „/”. std :: filesystem :: path jest standardowe i przenośne.
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

Może być konieczne p.parent_path().filename()uzyskanie nazwy folderu nadrzędnego.

srbcheema1
źródło
5

Użyj boost :: filesystem. I tak zostanie włączony do następnego standardu, więc równie dobrze możesz się do niego przyzwyczaić.

Edward Strange
źródło
1
O jakim standardzie mówisz? Wiem, że wiele rzeczy z boosta zostało dodanych do C ++ std lib, system plików też zostanie dodany?
McLeary,
7
„I tak zostanie włączony do następnego standardu”. I tak nie jest
Anton K.
@AntonK może C ++ 2017?
Alessandro Jacopson
6
@AlessandroJacopson Cool, wydaje się być zawarty w C ++ 17 - en.cppreference.com/w/cpp/filesystem
Anton K
1

_splitpath to fajne rozwiązanie CRT.

Ofek Shilon
źródło
1

Jestem tak zaskoczony, że nikt nie wspomniał o standardowym sposobie w Posix

Użyj basename / dirnamekonstrukcji.

mężczyzna basename

Utkarsh Kumar
źródło
Funkcje POSIX nie są pozbawione wad. W szczególności mogą modyfikować bufor, który przekazujesz (naprawdę mają na myśli, że podpis jest, basname(char * path)a nie basename(const char * path)), a implementacje, które tego nie robią, wydają się używać bufora statycznego, co czyni je niebezpiecznymi wątkowo (w zasadzie może również zwrócić dynamicznie przydzielane wyniki, ale to uzależnia od allocfunkcji rodzinnych, co jest niewygodne w C ++).
dmckee --- kociak byłego moderatora
-1

Standardowy C ++ nie zrobi zbyt wiele w tym zakresie, ponieważ nazwy ścieżek są specyficzne dla platformy. Możesz ręcznie przeanalizować ciąg (jak w odpowiedzi glowcodera), skorzystać z udogodnień systemu operacyjnego (np. Http://msdn.microsoft.com/en-us/library/aa364232(v=VS.85).aspx ) lub prawdopodobnie Najlepszym podejściem jest użycie biblioteki systemu plików innej firmy, takiej jak boost :: filesystem.

Tryb
źródło
Standardowy C ++ 1z obecnie próbuje zaadoptować bibliotekę systemu plików boost, co sprawia, że ​​przyjazność dla platformy jest znacznie mniejszym problemem. Przynajmniej nadal znajduje się w eksperymentalnych nagłówkach MSVC.
kayleeFrye_onDeck
-6

Po prostu użyj tego: ExtractFilePath (your_path_file_name)

fxPiotr
źródło
5
Uważam, że jest to metoda Delphi, a nie coś w C ++.
Ian Hunter