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

59

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

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

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

Теги:
file
folder

10 ответов

18
Лучший ответ

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

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

Использование 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()
  • 0
    Если вы также можете иметь дело с каталогами, помните о том, что parent_path() из "C:\\folder" приведет к "C:" .
Показать ещё 1 комментарий
40

Пример из 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;
}
  • 0
    Это лучшее минимальное решение здесь.
5

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

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

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

  • 1
    О каком стандарте вы говорите? Я знаю, что многое из boost было добавлено в C ++ std lib, файловая система тоже будет добавлена?
  • 7
    «В любом случае он будет включен в следующий стандарт»
Показать ещё 3 комментария
3

Согласно cppreference.com, существует экспериментальная поддержка библиотеки заголовков для С++ 17/1z с классом std::experimental::filesystem::path с использованием метода parent_path.

#include <iostream>
#include <experimental/filesystem>
namespace fs = std::experimental::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"
  • 0
    Также существует метод .remove_filename() .
2

_ splitpath - это хорошее решение для CRT.

1

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

  • 0
    Стандарт C ++ 1z в настоящее время пытается принять библиотеку файловой системы boost, что делает дружественность к платформе гораздо менее важной проблемой. Это все еще в экспериментальных заголовках для MSVC, по крайней мере.
0

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

Используйте конструкции basename / dirname.

man basename

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

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

  • 4
    Я считаю, что это метод Delphi, а не что-то в C ++.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню