How can I extract the file name and extension from a path in C++

C++StringFile Io

C++ Problem Overview


I have a list of files stored in a .log in this syntax:

c:\foto\foto2003\shadow.gif
D:\etc\mom.jpg

I want to extract the name and the extension from this files. Can you give a example of a simple way to do this?

C++ Solutions


Solution 1 - C++

To extract a filename without extension, use boost::filesystem::path::stem instead of ugly std::string::find_last_of(".")

boost::filesystem::path p("c:/dir/dir/file.ext");
std::cout << "filename and extension : " << p.filename() << std::endl; // file.ext
std::cout << "filename only          : " << p.stem() << std::endl;     // file

Solution 2 - C++

For C++17:

#include <filesystem>

std::filesystem::path p("c:/dir/dir/file.ext");
std::cout << "filename and extension: " << p.filename() << std::endl; // "file.ext"
std::cout << "filename only: " << p.stem() << std::endl;              // "file"

Reference about filesystem: http://en.cppreference.com/w/cpp/filesystem


As suggested by @RoiDanto, for the output formatting, std::out may surround the output with quotations, e.g.:

filename and extension: "file.ext"

You can convert std::filesystem::path to std::string by p.filename().string() if that's what you need, e.g.:

filename and extension: file.ext

Solution 3 - C++

If you want a safe way (i.e. portable between platforms and not putting assumptions on the path), I'd recommend to use boost::filesystem.

It would look somehow like this:

boost::filesystem::path my_path( filename );

Then you can extract various data from this path. Here's the documentation of path object.


BTW: Also remember that in order to use path like

c:\foto\foto2003\shadow.gif

you need to escape the \ in a string literal:

const char* filename = "c:\\foto\\foto2003\\shadow.gif";

Or use / instead:

const char* filename = "c:/foto/foto2003/shadow.gif";

This only applies to specifying literal strings in "" quotes, the problem doesn't exist when you load paths from a file.

Solution 4 - C++

You'll have to read your filenames from the file in std::string. You can use the string extraction operator of std::ostream. Once you have your filename in a std::string, you can use the std::string::find_last_of method to find the last separator.

Something like this:

std::ifstream input("file.log");
while (input)
{
    std::string path;
    input >> path;

    size_t sep = path.find_last_of("\\/");
    if (sep != std::string::npos)
        path = path.substr(sep + 1, path.size() - sep - 1);

    size_t dot = path.find_last_of(".");
    if (dot != std::string::npos)
    {
        std::string name = path.substr(0, dot);
        std::string ext  = path.substr(dot, path.size() - dot);
    }
    else
    {
        std::string name = path;
        std::string ext  = "";
    }
}
    

Solution 5 - C++

Not the code, but here is the idea:

  1. Read a std::string from the input stream (std::ifstream), each instance read will be the full path
  2. Do a find_last_of on the string for the \
  3. Extract a substring from this position to the end, this will now give you the file name
  4. Do a find_last_of for ., and a substring either side will give you name + extension.

Solution 6 - C++

The following trick to extract the file name from a file path with no extension in c++ (no external libraries required):

#include <iostream>
#include <string>

using std::string;

string getFileName(const string& s) {
char sep = '/';
#ifdef _WIN32
sep = '\\';
#endif
size_t i = s.rfind(sep, s.length());
if (i != string::npos) 
{
string filename = s.substr(i+1, s.length() - i);
size_t lastindex = filename.find_last_of("."); 
string rawname = filename.substr(0, lastindex); 
return(rawname);
}

return("");
}

int main(int argc, char** argv) {

string path = "/home/aymen/hello_world.cpp";
string ss = getFileName(path);
std::cout << "The file name is \"" << ss << "\"\n";
}

Solution 7 - C++

I also use this snippet to determine the appropriate slash character:

boost::filesystem::path slash("/");
	boost::filesystem::path::string_type preferredSlash = slash.make_preferred().native();

and then replace the slashes with the preferred slash for the OS. Useful if one is constantly deploying between Linux/Windows.

Solution 8 - C++

For linux or unix machines, the os has two functions dealing with path and file names. use man 3 basename to get more information about these functions. The advantage of using the system provided functionality is that you don't have to install boost or needing to write your own functions.

#include <libgen.h>
       char *dirname(char *path);
       char *basename(char *path);

Example code from the man page:

   char *dirc, *basec, *bname, *dname;
           char *path = "/etc/passwd";

           dirc = strdup(path);
           basec = strdup(path);
           dname = dirname(dirc);
           bname = basename(basec);
           printf("dirname=%s, basename=%s\n", dname, bname);

Because of the non-const argument type of the basename() function, it is a little bit non-straight forward using this inside C++ code. Here is a simple example from my code base:

string getFileStem(const string& filePath) const {
   char* buff = new char[filePath.size()+1];
   strcpy(buff, filePath.c_str());
   string tmp = string(basename(buff));
   string::size_type i = tmp.rfind('.');
   if (i != string::npos) {
      tmp = tmp.substr(0,i);
   }
   delete[] buff;
   return tmp;
}

The use of new/delete is not good style. I could have put it into a try/catch block in case something happened between the two calls.

Solution 9 - C++

Nickolay Merkin's and Yuchen Zhong's answers are great, but however from the comments you can see that it is not fully accurate.

The implicit conversion to std::string when printing will wrap the file name in quotations. The comments aren't accurate either.

path::filename() and path::stem() returns a new path object and path::string() returns a reference to a string. Thus something like std::cout << file_path.filename().string() << "\n" might cause problems with dangling reference since the string that the reference points to might have been destroyed.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionOctavianView Question on Stackoverflow
Solution 1 - C++Nickolay MerkinView Answer on Stackoverflow
Solution 2 - C++YuchenView Answer on Stackoverflow
Solution 3 - C++KosView Answer on Stackoverflow
Solution 4 - C++Sylvain DefresneView Answer on Stackoverflow
Solution 5 - C++NimView Answer on Stackoverflow
Solution 6 - C++Aymen AlsaadiView Answer on Stackoverflow
Solution 7 - C++svanschalkwykView Answer on Stackoverflow
Solution 8 - C++Kemin ZhouView Answer on Stackoverflow
Solution 9 - C++Nicholas MuliantoView Answer on Stackoverflow