0

I am using C++ in linux and I want to extract the parent folder from a path in C++ 14.

For example, I have the path likes

/home/abc/fi.mp4

My expected output be abc. How can I do it in C++

This is that I did

std::string getFolderName(const std::string &path) {
    size_t found = path.find_last_of("/\\");
    string foldername = ...
    return foldername;
}
wohlstad
  • 12,661
  • 10
  • 26
  • 39
KimHee
  • 728
  • 2
  • 12
  • 22

3 Answers3

2

I believe the experimental version of std::filesystem was already available in C++14.

If it is indeed available in your environment, you can use parent_path() to get the parent folder from a path. Then you can use the method filename() to extract the name without the full path specification.
In order to convert a path to std::string you can use the string() method.

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

int main(int argc, char const* argv[])
{
    std::string path_str = "/home/abc/fi.mp4";
    std::experimental::filesystem::path p = path_str;
    std::experimental::filesystem::path parent_p = p.parent_path();
    std::experimental::filesystem::path parent_p_fname = p.parent_path().filename();
    std::string result_str = parent_p_fname.string();
    std::cout << result_str << std::endl;
    return 0;
}

Output:

abc

You can further use parent_path() to climb up the hirarchy.

Once C++17 is available for you, you can simply drop the experimental prefix.

wohlstad
  • 12,661
  • 10
  • 26
  • 39
  • I updated my answer with info about the `path::filename()` method that extract the folder name without full path specification. – wohlstad Jul 09 '22 at 07:36
  • Afaik the experimental library headers are non standard, thus their function is not properly defined nor guaranteed. Probably shouldn't use them in production code. – JHBonarius Jul 09 '22 at 07:39
  • Thanks it looks good, but if my input is std::string, so how can I do it with the line std::experimental::filesystem::path p = "/home/abc/fi.mp4"; – KimHee Jul 09 '22 at 07:44
  • I've been using `std::experimental::filesystem` for some years (also in production code after testing), with 0 problems. It was also a good preparation for upgrading to C++17. Having said that - I agree some caution is required. I guess the OP can test it and decide how stable/reliable it is in his case. – wohlstad Jul 09 '22 at 07:45
  • @KimHee you can simply assign the `std::string` into the `path` variable. Will update my answer. To convert back - call `string()` method as I explained in my answer. – wohlstad Jul 09 '22 at 07:45
  • 1
    Here's a very good thread on `std::experimental`: https://stackoverflow.com/a/43322001/11770674 – RL-S Jul 11 '22 at 03:58
  • @RL-S Thanks for the link. I understand that in general `experimental` fetaures are something you can use "at your own risk". It is not guarenteed to be supported by all compilers, and might change when/if will be added to the standard. However - `experimental::filesystem` specifically came from boost (where it was used for some time), and also evetuanlly found it's way into the standard (with C++17). IMHO the risk of using it (if your compiler supports it) is very low. Of course testing is required, and some discretion when using it in production code. – wohlstad Jul 11 '22 at 05:11
  • 1
    I think using `std::experimental` from an *older* standard, for a feature that was fully included in a *newer* standard, is very safe. The link was for further reading for the OP author :) – RL-S Jul 11 '22 at 06:53
1

You probably want something like this:

// Returns the path-string without the filename part
std::string getFolderPath(const std::string &path) {
   const size_t found = path.find_last_of("/\\");
   return (found == string::npos) ? path : path.substr(0, found);
}

// Returns only the filename part (after the last slash)
std::string getFileName(const std::string & path) {
   const size_t found = path.find_last_of("/\\");
   return (found == string::npos) ? path : path.substr(found+1);
}
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • so if I want to get `home`, how can I change your code – KimHee Jul 09 '22 at 04:10
  • `getFolderName("/home/abc/fi.mp4")` will return "/home/abc". You could then call `getFolderName()` on that returned string (i.e. `getFolderName("/home/abc")`) and that will return "/home". – Jeremy Friesner Jul 09 '22 at 04:22
  • Come back to original question, `getFolderName("/home/abc/fi.mp4") will return "/home/abc"`==> But I want to get "abc" only – KimHee Jul 09 '22 at 07:45
  • I updated my answer -- call `getFolderPath("/home/abc/fi.mp4")` to get "/home/abc", then call `getFileName("/home/abc")` to get "abc". – Jeremy Friesner Jul 09 '22 at 12:58
  • I called both but it does not work for getFileName. Could you check it – KimHee Jul 10 '22 at 00:51
  • ok it worked with your new modification – KimHee Jul 10 '22 at 01:30
  • I tried to get `home` but it does not work as your suggestion >getFolderName("/home/abc/fi.mp4") will return "/home/abc". You could then call getFolderName() on that returned string (i.e. getFolderName("/home/abc")) and that will return "/home" – KimHee Jul 10 '22 at 01:45
0

The function getFolders will get you all the folder names in a path.

    #include <vector>
    #include <iostream>
    #include <string>
    #include <sstream>
    using namespace std;
    
vector<string> getFolders(const char* path) {
        vector<string> folders;
        stringstream ss;
        int i = 0, k = 0;
        char c;
        while ((c = *(path + i)) != '\0') {
            if (c != '/'){
                ss << c;
                ++k;
            }
            else {
                if (k > 0){
                    folders.push_back(ss.str());
                    ss.str(string());
                    k = 0;
                }
            }
            ++i;
        }
        return folders;
    }
    void main()
    {
        vector<string> folders = getFolders("/home/abc/fi.mp4");
        for (auto f : folders){
            cout << f << endl;
        }
    }
mmj
  • 139
  • 8