Отримання імені каталогу з імені файлу


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 також підтримується .
IIНевидимий

153

Використання 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()від "C:\\folder"призведе до "C:".
Semjon Mössinger

багато підсилювачів оновлюється до std, тому спробуйте також це .... включіть <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

39

У 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"

2
Існує також .remove_filename()метод.
Qqwy

1
Дякую, @Qqwy, це також дозволяє використовувати шлях до каталогу з цим методом, щоб отримати правильні та очікувані результати на відміну від підходу з відповіді
Herrgott

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 ::. Це все одно буде включено до наступного стандарту, щоб ви могли до цього звикнути.


1
Про який стандарт ви говорите? Я знаю, що до C ++ std lib було додано багато речей з boost, файлова система також буде додана?
McLeary

7
"Це все одно буде включено до наступного стандарту" І це не так
Антон К

@AntonK, можливо, C ++ 2017?
Алессандро Якопсон,

6
@AlessandroJacopson Круто, здається, він включений в C ++ 17 - en.cppreference.com/w/cpp/filesystem
Антон К


1

Я настільки здивований, що ніхто не згадував стандартний спосіб у Posix

Будь ласка, використовуйте basename / dirnameконструкції.

базове ім'я людини


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

-1

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


В даний час стандартний C ++ 1z намагається застосувати бібліотеку файлової системи boost, оскільки зараз зручність використання платформи стає набагато меншою проблемою. Принаймні, це все ще в експериментальних заголовках для MSVC.
kayleeFrye_onDeck

-6

Просто використовуйте це: ExtractFilePath (your_path_file_name)


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