Получение имени каталога из имени файла

85

У меня есть имя файла (C: \ folder \ foo.txt), и мне нужно получить имя папки (C: \ folder) в неуправляемом C ++. В C # я бы сделал что-то вроде этого:

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

Есть ли функция, которую можно использовать в неуправляемом C ++ для извлечения пути из имени файла?

Джон Тэкабери
источник

Ответы:

20

Для этого есть стандартная функция Windows, PathRemoveFileSpec . Если вы поддерживаете только Windows 8 и более поздние версии, настоятельно рекомендуется использовать вместо него PathCchRemoveFileSpec . Среди других улучшений, он больше не ограничен MAX_PATH(260) символами.

Андреас Рейбранд
источник
2
Обратите внимание, что эта функция теперь устарела. Компания Microsoft предлагает вместо этого использовать PathCchRemoveFileSpec .
По умолчанию,
1
@ По умолчанию: PathCchRemoveFileSpec доступен только начиная с Windows 8. Поскольку Windows Vista и 7 по-прежнему поддерживаются, PathRemoveFileSpec тоже .
Inspectable
154

Использование Boost.Filesystem:

boost::filesystem::path p("C:\\folder\\foo.txt");
boost::filesystem::path dir = p.parent_path();
Арак
источник
2
p.remove_filename()будет модифицироваться pна месте и может быть реализован более эффективно, чемp = p.parent_path()
Питер Кордес
Если вы также можете иметь дело с каталогами, имейте в виду, что parent_path()from "C:\\folder"приведет к "C:".
Семьон Мёссингер
много ускорения будет обновлено до std, так что попробуйте и это .... include <filesystem> .... std :: experimental :: filesystem :: path p ("C: \\ folder \\ foo.txt");
S Meaden
72

Пример из 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
источник
1
Это лучшее минимальное решение здесь.
Plasmacel
42

В C ++ 17 существует класс, std::filesystem::pathиспользующий метод 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';
}

Возможный выход:

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"
Алессандро Якопсон
источник
3
Также существует .remove_filename()метод.
Qqwy 06
1
Спасибо, @Qqwy, он также позволяет использовать путь к каталогу с этим методом для получения правильных и ожидаемых результатов, в отличие от подхода из ответа
Херрготт
13

Почему это должно быть так сложно?

#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
источник
10
Это не переносится. Разделитель путей в Linux - '/'. std :: filesystem :: path является стандартным и переносимым.
Реми
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

Возможно, вам потребуется p.parent_path().filename()получить имя родительской папки.

srbcheema1
источник
5

Используйте boost :: filesystem. В любом случае он будет включен в следующий стандарт, так что вы можете к нему привыкнуть.

Эдвард Стрэндж
источник
1
О каком стандарте вы говорите? Я знаю, что в C ++ std lib было добавлено много чего из boost, файловая система тоже будет добавлена?
McLeary 05
7
«Это все равно будет включено в следующий стандарт». И это не так,
Антон К
@AntonK может быть C ++ 2017?
Алессандро Якопсон
6
@AlessandroJacopson Круто, похоже, он включен в C ++ 17 - en.cppreference.com/w/cpp/filesystem
Антон К.
1

Я так удивлен, что никто не упомянул стандартный способ в Posix

Пожалуйста, используйте basename / dirnameконструкции.

мужское имя

Уткарш Кумар
источник
1
Функции POSIX не лишены недостатков. В частности, они могут изменять буфер, который вы передаете (они действительно означают, что сигнатура есть, basname(char * path)а нет basename(const char * path)), и реализации, которые этого не делают, по всей видимости, должны использовать статический буфер, что делает их потокобезопасными (в принципе, вы может также возвращать динамически распределяемые результаты, но это делает вас зависимыми от allocфункций семейства, что неудобно в C ++).
dmckee --- котенок экс-модератора
-1

Стандартный C ++ в этом отношении мало что поможет, поскольку имена путей зависят от платформы. Вы можете вручную разобрать строку (как в ответе светового кодера), использовать средства операционной системы (например, http://msdn.microsoft.com/en-us/library/aa364232(v=VS.85).aspx ) или, возможно, лучший подход, вы можете использовать стороннюю библиотеку файловой системы, например boost :: filesystem.

Зубчатое колесо
источник
Стандарт C ++ 1z в настоящее время пытается принять библиотеку файловой системы boost, и теперь удобство работы с платформой становится гораздо меньшей проблемой. По крайней мере, он все еще находится в экспериментальных заголовках для MSVC.
kayleeFrye_onDeck 08
-6

Просто используйте это: ExtractFilePath (your_path_file_name)

fxPiotr
источник
5
Я считаю, что это метод Delphi, а не что-то в C ++.
Ian Hunter