115

What is the simplest way to get the file name that from a path?

string filename = "C:\\MyDirectory\\MyFile.bat"

In this example, I should get "MyFile". without extension.

Prashant Kumar
  • 20,069
  • 14
  • 47
  • 63
nidhal
  • 1,609
  • 4
  • 16
  • 15

24 Answers24

93

The task is fairly simple as the base filename is just the part of the string starting at the last delimeter for folders:

std::string base_filename = path.substr(path.find_last_of("/\\") + 1)

If the extension is to be removed as well the only thing to do is find the last . and take a substr to this point

std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);

Perhaps there should be a check to cope with files solely consisting of extensions (ie .bashrc...)

If you split this up into seperate functions you're flexible to reuse the single tasks:

template<class T>
T base_name(T const & path, T const & delims = "/\\")
{
  return path.substr(path.find_last_of(delims) + 1);
}
template<class T>
T remove_extension(T const & filename)
{
  typename T::size_type const p(filename.find_last_of('.'));
  return p > 0 && p != T::npos ? filename.substr(0, p) : filename;
}

The code is templated to be able to use it with different std::basic_string instances (i.e. std::string & std::wstring...)

The downside of the templation is the requirement to specify the template parameter if a const char * is passed to the functions.

So you could either:

A) Use only std::string instead of templating the code

std::string base_name(std::string const & path)
{
  return path.substr(path.find_last_of("/\\") + 1);
}

B) Provide wrapping function using std::string (as intermediates which will likely be inlined / optimized away)

inline std::string string_base_name(std::string const & path)
{
  return base_name(path);
}

C) Specify the template parameter when calling with const char *.

std::string base = base_name<std::string>("some/path/file.ext");

Result

std::string filepath = "C:\\MyDirectory\\MyFile.bat";
std::cout << remove_extension(base_name(filepath)) << std::endl;

Prints

MyFile
Pixelchemist
  • 24,090
  • 7
  • 47
  • 71
  • In this use case everything is OK (and original question is answered), but your extension remover is not perfect - it will fail if we pass there something like "/home/user/my.dir/myfile" – avtomaton Aug 27 '15 at 08:03
  • @avtomaton The extension removing function should be used on a file name not a path. (Just apply `base_name` first.) – Pixelchemist Aug 27 '15 at 09:14
  • I understand it (that's why I wrote that original question is answered and in this use-case everything is OK). Just wanted to point out to this issue for somebody who will try to use these snippets. – avtomaton Aug 27 '15 at 10:14
  • Very nice explanation. It enhances the structural understanding of the problem. Thanks – hell_ical_vortex May 18 '16 at 07:15
  • The backslash is actually a valid filename character on Unix. Your solution gives the wrong result for the Unix path `/home/user/my\file.txt`. – eyelash Apr 12 '22 at 07:43
70

A possible solution:

string filename = "C:\\MyDirectory\\MyFile.bat";

// Remove directory if present.
// Do this before extension removal incase directory has a period character.
const size_t last_slash_idx = filename.find_last_of("\\/");
if (std::string::npos != last_slash_idx)
{
    filename.erase(0, last_slash_idx + 1);
}

// Remove extension if present.
const size_t period_idx = filename.rfind('.');
if (std::string::npos != period_idx)
{
    filename.erase(period_idx);
}
hmjd
  • 120,187
  • 20
  • 207
  • 252
65

The Simplest way in C++17 is:

use the #include <filesystem> and filename() for filename with extension and stem() without extension.

#include <iostream>
#include <string>
#include <filesystem>
namespace fs = std::filesystem;

int main()
{
  std::string filename = "C:\\MyDirectory\\MyFile.bat";

  std::cout << fs::path(filename).filename() << '\n'
    << fs::path(filename).stem() << '\n'
    << fs::path("/foo/bar.txt").filename() << '\n'
    << fs::path("/foo/bar.txt").stem() << '\n'
    << fs::path("/foo/.bar").filename() << '\n'
    << fs::path("/foo/bar/").filename() << '\n'
    << fs::path("/foo/.").filename() << '\n'
    << fs::path("/foo/..").filename() << '\n'
    << fs::path(".").filename() << '\n'
    << fs::path("..").filename() << '\n'
    << fs::path("/").filename() << '\n';
}

Which can be compiled with g++ -std=c++17 main.cpp -lstdc++fs, and outputs:

"MyFile.bat"
"MyFile"
"bar.txt"
"bar"
".bar"
""
"."
".."
"."
".."
"/"

Reference: cppreference

Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
eliasetm
  • 1,399
  • 11
  • 20
40

The simplest solution is to use something like boost::filesystem. If for some reason this isn't an option...

Doing this correctly will require some system dependent code: under Windows, either '\\' or '/' can be a path separator; under Unix, only '/' works, and under other systems, who knows. The obvious solution would be something like:

std::string
basename( std::string const& pathname )
{
    return std::string( 
        std::find_if( pathname.rbegin(), pathname.rend(),
                      MatchPathSeparator() ).base(),
        pathname.end() );
}

, with MatchPathSeparator being defined in a system dependent header as either:

struct MatchPathSeparator
{
    bool operator()( char ch ) const
    {
        return ch == '/';
    }
};

for Unix, or:

struct MatchPathSeparator
{
    bool operator()( char ch ) const
    {
        return ch == '\\' || ch == '/';
    }
};

for Windows (or something still different for some other unknown system).

EDIT: I missed the fact that he also wanted to suppress the extention. For that, more of the same:

std::string
removeExtension( std::string const& filename )
{
    std::string::const_reverse_iterator
                        pivot
            = std::find( filename.rbegin(), filename.rend(), '.' );
    return pivot == filename.rend()
        ? filename
        : std::string( filename.begin(), pivot.base() - 1 );
}

The code is a little bit more complex, because in this case, the base of the reverse iterator is on the wrong side of where we want to cut. (Remember that the base of a reverse iterator is one behind the character the iterator points to.) And even this is a little dubious: I don't like the fact that it can return an empty string, for example. (If the only '.' is the first character of the filename, I'd argue that you should return the full filename. This would require a little bit of extra code to catch the special case.) }

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 10
    How about using `string::find_last_of` instead of manipulating reverse iterators? – Luc Touraille Dec 15 '11 at 14:04
  • @LucTouraille Why learn two ways of doing things when one will do? You'd need the reverse iterators for any container except `string`, so you have to learn them anyway. And having learned them, there's no reason to bother learning all of the bloated interface to `std::string`. – James Kanze Dec 15 '11 at 15:35
  • Note: The [](https://msdn.microsoft.com/en-us/library/hh874694.aspx) header ships with Visual Studio 2015 and above, so you don't have to add a dependency on boost to use it. – IInspectable May 02 '17 at 10:39
32

_splitpath should do what you need. You could of course do it manually but _splitpath handles all special cases as well.

EDIT:

As BillHoag mentioned it is recommended to use the more safe version of _splitpath called _splitpath_s when available.

Or if you want something portable you could just do something like this

std::vector<std::string> splitpath(
  const std::string& str
  , const std::set<char> delimiters)
{
  std::vector<std::string> result;

  char const* pch = str.c_str();
  char const* start = pch;
  for(; *pch; ++pch)
  {
    if (delimiters.find(*pch) != delimiters.end())
    {
      if (start != pch)
      {
        std::string str(start, pch);
        result.push_back(str);
      }
      else
      {
        result.push_back("");
      }
      start = pch + 1;
    }
  }
  result.push_back(start);

  return result;
}

...
std::set<char> delims{'\\'};

std::vector<std::string> path = splitpath("C:\\MyDirectory\\MyFile.bat", delims);
cout << path.back() << endl;
Shrout1
  • 2,497
  • 4
  • 42
  • 65
AndersK
  • 35,813
  • 6
  • 60
  • 86
  • 2
    There's no `_splitpath` in any of the includes on my machine. – James Kanze Dec 15 '11 at 13:22
  • @JamesKanze maybe you dont have Visual Studio as the OP? – AndersK Dec 15 '11 at 13:41
  • 10
    I have Visual Studio, **and** g++, and Sun CC. Why should I use something non-standard when there are perfectly good portable solutions. – James Kanze Dec 15 '11 at 13:49
  • I guess it depends on the requirements. – AndersK Dec 16 '11 at 06:13
  • 2
    @James, the page linked to says it is in ``. As for portability, perhaps you can list some examples of “perfectly good portable solutions”? – Synetech Apr 04 '12 at 03:35
  • @Anders, what do you mean by “special cases”? The page doesn’t mention UNC paths which I suppose is one sort of special case. – Synetech Apr 04 '12 at 03:36
  • 2
    @Synetech The page linked to describes a Microsoft extension, not ``. And the mot obvious portable solution is `boost::filesystem`. – James Kanze Apr 04 '12 at 07:19
  • 3
    @James, you don’t have `_splitpath` in the `stdlib.h` of your copy of VS? Then you may want to do a repair install of VS. – Synetech Apr 04 '12 at 19:05
  • Boost would be the way to go. If your code base is already using Windows APIs and you want to be consistent, use the more secure `_splitpath_s` rather than '_splitpath'. – Bill Hoag Nov 25 '13 at 21:37
  • @BillHoag good point, will add that. As to boost, well sometimes boost is not available e.g. where I work boost is not allowed (yeah I know). – AndersK Nov 26 '13 at 06:38
17

If you can use boost,

#include <boost/filesystem.hpp>
boost::filesystem::path p("C:\\MyDirectory\\MyFile.bat");
string basename = p.filename().string();
//or 
//string basename = boost::filesystem::path("C:\\MyDirectory\\MyFile.bat").filename().string();

This is all.

I recommend you to use boost library. Boost gives you a lot of conveniences when you work with C++. It supports almost all platforms. If you use Ubuntu, you can install boost library by only one line sudo apt-get install libboost-all-dev (ref. How to install Boost on Ubuntu)

keineahnung2345
  • 2,635
  • 4
  • 13
  • 28
plhn
  • 5,017
  • 4
  • 47
  • 47
16

You can also use the shell Path APIs PathFindFileName, PathRemoveExtension. Probably worse than _splitpath for this particular problem, but those APIs are very useful for all kinds of path parsing jobs and they take UNC paths, forward slashes and other weird stuff into account.

wstring filename = L"C:\\MyDirectory\\MyFile.bat";
wchar_t* filepart = PathFindFileName(filename.c_str());
PathRemoveExtension(filepart); 

http://msdn.microsoft.com/en-us/library/windows/desktop/bb773589(v=vs.85).aspx

The drawback is that you have to link to shlwapi.lib, but I'm not really sure why that's a drawback.

Skrymsli
  • 5,173
  • 7
  • 34
  • 36
14

Function:

#include <string>

std::string
basename(const std::string &filename)
{
    if (filename.empty()) {
        return {};
    }

    auto len = filename.length();
    auto index = filename.find_last_of("/\\");

    if (index == std::string::npos) {
        return filename;
    }

    if (index + 1 >= len) {

        len--;
        index = filename.substr(0, len).find_last_of("/\\");

        if (len == 0) {
            return filename;
        }

        if (index == 0) {
            return filename.substr(1, len - 1);
        }

        if (index == std::string::npos) {
            return filename.substr(0, len);
        }

        return filename.substr(index + 1, len - index - 1);
    }

    return filename.substr(index + 1, len - index);
}

Tests:

#define CATCH_CONFIG_MAIN
#include <catch/catch.hpp>

TEST_CASE("basename")
{
    CHECK(basename("") == "");
    CHECK(basename("no_path") == "no_path");
    CHECK(basename("with.ext") == "with.ext");
    CHECK(basename("/no_filename/") == "no_filename");
    CHECK(basename("no_filename/") == "no_filename");
    CHECK(basename("/no/filename/") == "filename");
    CHECK(basename("/absolute/file.ext") == "file.ext");
    CHECK(basename("../relative/file.ext") == "file.ext");
    CHECK(basename("/") == "/");
    CHECK(basename("c:\\windows\\path.ext") == "path.ext");
    CHECK(basename("c:\\windows\\no_filename\\") == "no_filename");
}
Rian Quinn
  • 1,766
  • 12
  • 22
9

From C++ Docs - string::find_last_of

#include <iostream>       // std::cout
#include <string>         // std::string

void SplitFilename (const std::string& str) {
  std::cout << "Splitting: " << str << '\n';
  unsigned found = str.find_last_of("/\\");
  std::cout << " path: " << str.substr(0,found) << '\n';
  std::cout << " file: " << str.substr(found+1) << '\n';
}

int main () {
  std::string str1 ("/usr/bin/man");
  std::string str2 ("c:\\windows\\winhelp.exe");

  SplitFilename (str1);
  SplitFilename (str2);

  return 0;
}

Outputs:

Splitting: /usr/bin/man
 path: /usr/bin
 file: man
Splitting: c:\windows\winhelp.exe
 path: c:\windows
 file: winhelp.exe
jave.web
  • 13,880
  • 12
  • 91
  • 125
  • Don't forget (and to handle) that `find_last_of` returns `string::npos` if nothing was found. – congusbongus Mar 14 '16 at 23:53
  • @congusbongus True, but there is no sense of splitting the file path when it is just a file name (without path) :) – jave.web Mar 15 '16 at 11:05
  • @jave.web It does make sense and MUST handle returns 'string::npos'. Implementing a function for this should be able to handle different inputs including "just filename". Otherwise, it will be useless if its buggy in actual implementation. – winux Sep 22 '16 at 08:09
  • @winux This considers already **valid** *PATHS*... If you don't trust the input, you should, of course, validate the path first. – jave.web Sep 23 '16 at 13:07
  • @winux **Anyway** the check for `string::npos` does not need to be done because of the way how this and `string::substr` are implemented. a) `string::npos` is passed as "length" => `substr` has documented behaviour of reading all until end. b) `substr` is given "`string::npos + 1`" and no length: `string::npos` is documented to have a value of `-1`, so that evaluates to `0` => start of the string and lengths' default value for `substr` is the `npos` => works on "just filename" too http://www.cplusplus.com/reference/string/string/substr/ http://www.cplusplus.com/reference/string/string/npos/ – jave.web Sep 23 '16 at 13:07
7

C++11 variant (inspired by James Kanze's version) with uniform initialization and anonymous inline lambda.

std::string basename(const std::string& pathname)
{
    return {std::find_if(pathname.rbegin(), pathname.rend(),
                         [](char c) { return c == '/'; }).base(),
            pathname.end()};
}

It does not remove the file extension though.

Volomike
  • 23,743
  • 21
  • 113
  • 209
alveko
  • 2,109
  • 2
  • 18
  • 15
  • Short and sweet, although it only works with non-Windows paths. – Volomike Mar 05 '16 at 18:33
  • you can always change lambda return to `return c == '/' || c == '\\';` to make it works on windows – rejkowic Apr 19 '16 at 09:57
  • To handle paths such as "", "///", and "dir1/dir2/", add the following code before the return statement above (cf. POSIX basename()): `if (pathname.size() == 0) return "."; auto iter = pathname.rbegin(); auto rend = pathname.rend(); while (iter != rend && *iter == '/') ++iter; if (iter == rend) /* pathname has only path separators */ return "/"; pathname = std::string(pathname.begin(), iter.base());` – Gidfiddle Oct 27 '17 at 06:14
6

The boost filesystem library is also available as the experimental/filesystem library and was merged into ISO C++ for C++17. You can use it like this:

#include <iostream>
#include <experimental/filesystem>

namespace fs = std::experimental::filesystem;

int main () {
    std::cout << fs::path("/foo/bar.txt").filename() << '\n'
}

Output:

"bar.txt"

It also works for std::string objects.

Adam Erickson
  • 6,027
  • 2
  • 46
  • 33
4

this is the only thing that actually finally worked for me:

#include "Shlwapi.h"

CString some_string = "c:\\path\\hello.txt";
LPCSTR file_path = some_string.GetString();
LPCSTR filepart_c = PathFindFileName(file_path);
LPSTR filepart = LPSTR(filepart_c);
PathRemoveExtension(filepart);

pretty much what Skrymsli suggested but doesn't work with wchar_t*, VS Enterprise 2015

_splitpath worked as well, but I don't like having to guess at how many char[?] characters I'm going to need; some people probably need this control, i guess.

CString c_model_name = "c:\\path\\hello.txt";
char drive[200];
char dir[200];
char name[200];
char ext[200];
_splitpath(c_model_name, drive, dir, name, ext);

I don't believe any includes were needed for _splitpath. No external libraries (like boost) were needed for either of these solutions.

Fractal
  • 1,748
  • 5
  • 26
  • 46
4
std::string getfilename(std::string path)
{
    path = path.substr(path.find_last_of("/\\") + 1);
    size_t dot_i = path.find_last_of('.');
    return path.substr(0, dot_i);
}
Beyondo
  • 2,952
  • 1
  • 17
  • 42
3

You can use the std::filesystem to do it quite nicely:

#include <filesystem>
namespace fs = std::experimental::filesystem;

fs::path myFilePath("C:\\MyDirectory\\MyFile.bat");
fs::path filename = myFilePath.stem();
GuidedHacking
  • 3,628
  • 1
  • 9
  • 59
3

I would do it by...

Search backwards from the end of the string until you find the first backslash/forward slash.

Then search backwards again from the end of the string until you find the first dot (.)

You then have the start and end of the file name.

Simples...

TomP89
  • 1,420
  • 3
  • 11
  • 29
  • Which doesn't work for any system I know. (The one system which accepts `'\\'` as a path separator **also** uses `'/'`, so you need to match either.) And I'm not sure what you'd be looking forward for. – James Kanze Dec 15 '11 at 13:32
  • Okay so modify it to match either, no biggy. And looking forward for the first dot (.) – TomP89 Dec 15 '11 at 13:48
  • You still have to find the last dot, not the first. (Reverse iterators are your friend!) – James Kanze Dec 15 '11 at 13:56
  • Ah yes, good point. So for a file.ext.ext then you would want to extract file.ext wouldn't you. :) – TomP89 Dec 15 '11 at 13:57
  • Presumably. That's the usual convention, in any case: `my.source.cpp` gets compiled to `my.source.obj`, for example (with the extension `.cpp` replaced with `.obj`). – James Kanze Dec 15 '11 at 14:02
2

Dont use _splitpath() and _wsplitpath(). They are not safe, and they are obsolete!

Instead, use their safe versions, namely _splitpath_s() and _wsplitpath_s()

hkBattousai
  • 10,583
  • 18
  • 76
  • 124
2
m_szFilePath.MakeLower();
CFileFind finder;
DWORD buffSize = MAX_PATH;
char longPath[MAX_PATH];
DWORD result = GetLongPathName(m_szFilePath, longPath, MAX_PATH );

if( result == 0)
{
    m_bExists = FALSE;
    return;
}
m_szFilePath = CString(longPath);
m_szFilePath.Replace("/","\\");
m_szFilePath.Trim();
//check if it does not ends in \ => remove it
int length = m_szFilePath.GetLength();
if( length > 0 && m_szFilePath[length - 1] == '\\' )
{
    m_szFilePath.Truncate( length - 1 );
}
BOOL bWorking = finder.FindFile(this->m_szFilePath);
if(bWorking){
    bWorking = finder.FindNextFile();
    finder.GetCreationTime(this->m_CreationTime);
    m_szFilePath = finder.GetFilePath();
    m_szFileName = finder.GetFileName();

    this->m_szFileExtension = this->GetExtension( m_szFileName );

    m_szFileTitle = finder.GetFileTitle();
    m_szFileURL = finder.GetFileURL();
    finder.GetLastAccessTime(this->m_LastAccesTime);
    finder.GetLastWriteTime(this->m_LastWriteTime);
    m_ulFileSize = static_cast<unsigned long>(finder.GetLength());
    m_szRootDirectory = finder.GetRoot();
    m_bIsArchive = finder.IsArchived();
    m_bIsCompressed = finder.IsCompressed();
    m_bIsDirectory = finder.IsDirectory();
    m_bIsHidden = finder.IsHidden();
    m_bIsNormal = finder.IsNormal();
    m_bIsReadOnly = finder.IsReadOnly();
    m_bIsSystem = finder.IsSystem();
    m_bIsTemporary = finder.IsTemporary();
    m_bExists = TRUE;
    finder.Close();
}else{
    m_bExists = FALSE;
}

The variable m_szFileName contains the fileName.

Lucian
  • 3,407
  • 2
  • 21
  • 18
  • 3
    wow - that's a lot of code for "get file name" from path... :) – Nim Dec 15 '11 at 13:15
  • 4
    @Nim My impression as well. In my own code, I use a one-liner: `boost::filesystem::path( path ).filename()`. – James Kanze Dec 15 '11 at 13:36
  • I have a CFileInfo class that has that code. I just dumped the code here because it is tested and I did not want to risk anything... You could just use about 5 lines of code from this example. – Lucian Dec 15 '11 at 13:42
2

This should work too :

// strPath = "C:\\Dir\\File.bat" for example
std::string getFileName(const std::string& strPath)
{
    size_t iLastSeparator = 0;
    return strPath.substr((iLastSeparator = strPath.find_last_of("\\")) != std::string::npos ? iLastSeparator + 1 : 0, strPath.size() - strPath.find_last_of("."));
}

If you can use it, Qt provide QString (with split, trim etc), QFile, QPath, QFileInfo etc to manipulate files, filenames and directories. And of course it's also cross plaftorm.

Prashant Kumar
  • 20,069
  • 14
  • 47
  • 63
typedef
  • 1,159
  • 1
  • 6
  • 11
  • 4
    For the sake of the future readers of your code, please use temporary variables with meaningful names instead of stuffing everything into a single line of code (and while you're at it, please encapsulate all this into a function `getFilename` or something like that). – Luc Touraille Dec 15 '11 at 14:07
  • edited. But the point was to make it short, as several working answers already have been given. – typedef Dec 15 '11 at 14:32
  • 1
    I think it is WRONG. Should not you replace the last part: "strPath.size() - strPath.find_last_of(".")" by "strPath.find_last_of(".") - iLastSeparator" – azerty Jan 22 '14 at 12:35
  • @taktak004 you are right, it should be ` return strPath.substr( (iLastSeparator = strPath.find_last_of("/")) != std::string::npos ? iLastSeparator + 1 : 0, strPath.find_last_of(".") - iLastSeparator );` – phenmod Dec 08 '15 at 10:08
1

A really simple and short function that returns the filename+path that I made which uses no dependencies:

const char* GetFileNameFromPath(const char* _buffer)
{
    char c;
    int  i;
    for (i = 0; ;++i) {
        c = *((char*)_buffer+i);
        if (c == '\\' || c == '/')
            return GetFileNameFromPath((char*)_buffer + i + 1);
        if (c == '\0')
            return _buffer;
    }
    return "";
}

To only get the filename without the extension you could change c == '\0' to c == '.'.

Alex
  • 840
  • 7
  • 23
1

Here is the simplest version:

#include <iostream>
#include <string>

int main()
{
  std::string filepath = "directory/file-name.txt";
    
  std::string filename = filepath.substr(filepath.find_last_of("/")+1, filepath.find_last_of(".") - filepath.find_last_of("/") - 1);
  
  std::cout << filename << std::endl;
}

Returns:

file-name
0

For long time I was looking for a function able to properly decompose file path. For me this code is working perfectly for both Linux and Windows.

void decomposePath(const char *filePath, char *fileDir, char *fileName, char *fileExt)
{
    #if defined _WIN32
        const char *lastSeparator = strrchr(filePath, '\\');
    #else
        const char *lastSeparator = strrchr(filePath, '/');
    #endif

    const char *lastDot = strrchr(filePath, '.');
    const char *endOfPath = filePath + strlen(filePath);
    const char *startOfName = lastSeparator ? lastSeparator + 1 : filePath;
    const char *startOfExt = lastDot > startOfName ? lastDot : endOfPath;

    if(fileDir)
        _snprintf(fileDir, MAX_PATH, "%.*s", startOfName - filePath, filePath);

    if(fileName)
        _snprintf(fileName, MAX_PATH, "%.*s", startOfExt - startOfName, startOfName);

    if(fileExt)
        _snprintf(fileExt, MAX_PATH, "%s", startOfExt);
}

Example results are:

[]
  fileDir:  ''
  fileName: ''
  fileExt:  ''

[.htaccess]
  fileDir:  ''
  fileName: '.htaccess'
  fileExt:  ''

[a.exe]
  fileDir:  ''
  fileName: 'a'
  fileExt:  '.exe'

[a\b.c]
  fileDir:  'a\'
  fileName: 'b'
  fileExt:  '.c'

[git-archive]
  fileDir:  ''
  fileName: 'git-archive'
  fileExt:  ''

[git-archive.exe]
  fileDir:  ''
  fileName: 'git-archive'
  fileExt:  '.exe'

[D:\Git\mingw64\libexec\git-core\.htaccess]
  fileDir:  'D:\Git\mingw64\libexec\git-core\'
  fileName: '.htaccess'
  fileExt:  ''

[D:\Git\mingw64\libexec\git-core\a.exe]
  fileDir:  'D:\Git\mingw64\libexec\git-core\'
  fileName: 'a'
  fileExt:  '.exe'

[D:\Git\mingw64\libexec\git-core\git-archive.exe]
  fileDir:  'D:\Git\mingw64\libexec\git-core\'
  fileName: 'git-archive'
  fileExt:  '.exe'

[D:\Git\mingw64\libexec\git.core\git-archive.exe]
  fileDir:  'D:\Git\mingw64\libexec\git.core\'
  fileName: 'git-archive'
  fileExt:  '.exe'

[D:\Git\mingw64\libexec\git-core\git-archiveexe]
  fileDir:  'D:\Git\mingw64\libexec\git-core\'
  fileName: 'git-archiveexe'
  fileExt:  ''

[D:\Git\mingw64\libexec\git.core\git-archiveexe]
  fileDir:  'D:\Git\mingw64\libexec\git.core\'
  fileName: 'git-archiveexe'
  fileExt:  ''

I hope this helps you also :)

no one special
  • 1,608
  • 13
  • 32
0

shlwapi.lib/dll uses the HKCU registry hive internally.

It's best not to link to shlwapi.lib if you're creating a library or the product does not have a UI. If you're writing a lib then your code can be used in any project including those that don't have UIs.

If you're writing code that runs when a user is not logged in (e.g. service [or other] set to start at boot or startup) then there's no HKCU. Lastly, shlwapi are settlement functions; and as a result high on the list to deprecate in later versions of Windows.

rguerin
  • 2,068
  • 14
  • 29
Brook
  • 1
0

A slow but straight forward regex solution:

    std::string file = std::regex_replace(path, std::regex("(.*\\/)|(\\..*)"), "");
Sava B.
  • 1,007
  • 1
  • 10
  • 21
0

I implemented a function that might meet your needs. It is based on string_view's constexpr function find_last_of (since c++17) which can be calculated at compile time

constexpr const char* base_filename(const char* p) {
    const size_t i = std::string_view(p).find_last_of('/');
    return std::string_view::npos == i ? p : p + i + 1 ;
}

//in the file you used this function
base_filename(__FILE__);