101

I have a filename (C:\folder\foo.txt) and I need to retrieve the folder name (C:\folder) in C++. In C# I would do something like this:

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

Is there a function that can be used in C++ to extract the path from the filename?

Community
  • 1
  • 1
Jon Tackabury
  • 47,710
  • 52
  • 130
  • 168

11 Answers11

165

Using Boost.Filesystem:

boost::filesystem::path p("C:\\folder\\foo.txt");
boost::filesystem::path dir = p.parent_path();
Khaled Alshaya
  • 94,250
  • 39
  • 176
  • 234
  • 2
    `p.remove_filename()` will modify `p` in-place, and [may be implemented more efficiently than `p = p.parent_path()`](http://www.boost.org/doc/libs/1_60_0/libs/filesystem/doc/reference.html#path-remove_filename) – Peter Cordes Feb 09 '16 at 02:14
  • If you might also deal with directories be aware of the fact that `parent_path()` from `"C:\\folder"` will result in `"C:"`. – Semjon Mössinger May 17 '16 at 12:43
  • lots of boost gets upgraded to std so also try this .... include .... std::experimental::filesystem::path p("C:\\folder\\foo.txt"); – S Meaden May 26 '17 at 15:44
84

Example from 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
  • 81,495
  • 25
  • 153
  • 204
61

In C++17 there exists a class std::filesystem::path using the method 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';
}

Possible output:

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"
Zitrax
  • 19,036
  • 20
  • 88
  • 110
Alessandro Jacopson
  • 18,047
  • 15
  • 98
  • 153
  • 4
    There also exists a `.remove_filename()` method. – Qqwy Mar 06 '19 at 12:43
  • 2
    Thanks, @Qqwy, it also allows to use directory path with that method to get correct and expected results unlike the approach from the answer – IC_ Sep 03 '20 at 06:26
  • Just be aware that `std::filesystem::path` may use `std::wstring` instead of `std::string` on some platforms, making it difficult to interoperate with normal strings... – David Given Jul 25 '23 at 21:34
24

There is a standard Windows function for this, PathRemoveFileSpec. If you only support Windows 8 and later, it is highly recommended to use PathCchRemoveFileSpec instead. Among other improvements, it is no longer limited to MAX_PATH (260) characters.

IInspectable
  • 46,945
  • 8
  • 85
  • 181
Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • 3
    Note that this function is now deprecated. The suggestion from Microsoft is to use [PathCchRemoveFileSpec](https://msdn.microsoft.com/en-us/library/hh707092%28v=vs.85%29.aspx) instead. – default Mar 07 '16 at 17:15
  • 1
    @Default: [PathCchRemoveFileSpec](https://msdn.microsoft.com/en-us/library/hh707092.aspx) is only available starting with Windows 8. Since Windows Vista and 7 are still supported, so is [PathRemoveFileSpec](https://msdn.microsoft.com/en-us/library/bb773748.aspx). – IInspectable Jul 31 '16 at 16:51
17

Why does it have to be so complicated?

#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
  • 2,287
  • 1
  • 26
  • 32
  • 17
    This is not portable. Path separator in linux is '/'. std::filesystem::path is standard and portable. – Rémi Jun 02 '18 at 16:49
  • Its not robust, but can be made portable. Portability is not always required. I prefer this than ask someone to upgrade to C++17 or install Boost. – brookbot Aug 29 '23 at 21:50
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

You may need p.parent_path().filename() to get name of parent folder.

srbcheema1
  • 544
  • 4
  • 16
4

Use boost::filesystem. It will be incorporated into the next standard anyway so you may as well get used to it.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
3

I'm so surprised no one has mentioned the standard way in Posix

Please use basename / dirname constructs.

man basename

Utkarsh Kumar
  • 567
  • 1
  • 5
  • 17
  • 1
    The POSIX functions are not without their drawbacks. In particular they may modify the buffer that you pass in, (they really do mean the signature is `basname(char * path)` and not `basename(const char * path)`), and implementations that don't do that appear to have to use a static buffer which makes them thread-unsafe (in principle you could also return dynamically allocated results, but that makes you dependent on `alloc` family functions which is awkward in C++). – dmckee --- ex-moderator kitten Dec 18 '18 at 18:24
1

_splitpath is a nice CRT solution.

Ofek Shilon
  • 14,734
  • 5
  • 67
  • 101
-1

Standard C++ won't do much for you in this regard, since path names are platform-specific. You can manually parse the string (as in glowcoder's answer), use operating system facilities (e.g. http://msdn.microsoft.com/en-us/library/aa364232(v=VS.85).aspx ), or probably the best approach, you can use a third-party filesystem library like boost::filesystem.

Cogwheel
  • 22,781
  • 4
  • 49
  • 67
  • The standard's C++1z is currently attempting to adopt the boost filesystem library as of now making platform-friendliness much less of an issue. It's still in the experimental headers for MSVC, at least. – kayleeFrye_onDeck Jul 08 '17 at 03:18
-6

Just use this: ExtractFilePath(your_path_file_name)

fxPiotr
  • 1
  • 1