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


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

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
}

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()получить имя родительской папки.


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)


5
Я считаю, что это метод Delphi, а не что-то в C ++.
Ian Hunter
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.