123

Does C++ have any equivalent to python's function os.path.join? Basically, I'm looking for something that combines two (or more) parts of a file path so that you don't have to worry about making sure the two parts fit together perfectly. If it's in Qt, that would be cool too.

Basically I spent an hour debugging some code and at least part of it was because root + filename had to be root/ + filename, and I'm looking to avoid that in the future.

1

8 Answers 8

117

Only as part of boost::filesystem library. Here is an example:

#include <iostream>
#include <boost/filesystem.hpp>

namespace fs = boost::filesystem;

int main ()
{
    fs::path dir ("/tmp");
    fs::path file ("foo.txt");
    fs::path full_path = dir / file;
    std::cout << full_path << std::endl;
    return 0;
}

Here is an example of compiling and running (platform specific):

$ g++ ./test.cpp -o test -lboost_filesystem -lboost_system
$ ./test 
/tmp/foo.txt
Sign up to request clarification or add additional context in comments.

11 Comments

This is also in TR2, which is likely to start shipping with compilers next year.
@Vlad : Yeah, it's not easily discoverable, but I hate clicking on Boost doc links and belatedly realizing I'm looking at an old version, so I edit people's version-specific links when I come across them. :-P
@ildjarn: Which seems to work great now... but wait until they change something about the site or the docs for the given library. It is worse than leaving the version-specific link from then on.
@Fred : Obviously if the functionality or question happens to be version-specific, I don't change the URL. In this case, it isn't, so I did.
With C++17 use #include <filesystem>, namespace fs = std::filesystem;. With C++14 use #include <experimental/filesystem>, namespace fs = std::experimental::filesystem;.
|
77

Similar to this answer (but not using boost), this functionality is available as part of std::filesystem. The following code compiles using Homebrew GCC 9.2.0_1 and using the flag --std=c++17:

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

int main() 
{
    fs::path dir ("/tmp");
    fs::path file ("foo.txt");
    fs::path full_path = dir / file;
    std::cout << full_path << std::endl;
    return 0;
}

5 Comments

As of C++17, this was merged into the (non-experimental) <filesystem> header. See en.cppreference.com/w/cpp/filesystem.
Thank you so much, this answer is a way to go in case if c++ 17 is available on the project.
in my case its just joining them directly (and not putting a / or \ between them)
@0xB00B just use A + B, fs::path override the operator / / + to append/concat. see reference en.cppreference.com/w/cpp/filesystem/path/concat
@Kevin Chan i was already talking about that, it wasn't working properly due to some implementation bug.
54

Since the question mentions Qt, check out QDir for that:

QString path = QDir(dirPath).filePath(fileName);

3 Comments

Beware that Qt is GPL. Might be a deal breaker for many uses.
@RustyX it is LGPL, to be precise.
i love this answer, no futzing with strings and concatenation, let the framework figure it out.
9

At least in Unix / Linux, it's always safe to join parts of a path by /, even if some parts of the path already end in /, i.e. root/path is equivalent to root//path.

In this case, all you really need is to join things on /. That said, I agree with other answers that boost::filesystem is a good choice if it is available to you because it supports multiple platforms.

1 Comment

QT is agnostic to path separator. If you print absolute path of a file on Windows the output is "C:/Users/Name/MyFile.txt" with the / (unix) separator. boost::filesystem is great but, in my opinion, if the project is Qt-based there's no need to add a dependecy for boost library.
7

If you want to do this with Qt, you can use QFileInfo constructor:

QFileInfo fi( QDir("/tmp"), "file" );
QString path = fi.absoluteFilePath();

Comments

5

With C++11 and Qt you can do this:

QString join(const QString& v) {
    return v;
}

template<typename... Args>
QString join(const QString& first, Args... args) {
    return QDir(first).filePath(join(args...));
}

Usage:

QString path = join("/tmp", "dir", "file"); // /tmp/dir/file

Comments

4

In Qt, just use / in code when using Qt API (QFile, QFileInfo). It will do the right thing on all platforms. If you have to pass a path to a non-Qt function, or want to format it for displaying it to the user, use QDir:toNativeSeparators() e.g.:

QDir::toNativeSeparators( path );

It will replace / by the native equivalent (i.e. \ on Windows). The other direction is done via QDir::fromNativeSeparators().

Comments

2

Here is a very simple C++11 friendly alternative for the people who have neither Boost, Qt nor C++17 (taken from here).

std::string pathJoin(const std::string& p1, const std::string& p2)
{
    char sep = '/';
    std::string tmp = p1;

#ifdef _WIN32
    sep = '\\';
#endif

    // Add separator if it is not included in the first path:
    if (p1[p1.length() - 1] != sep) {
        tmp += sep;
        return tmp + p2;
    } else {
        return p1 + p2;
    }
}

2 Comments

Did you test the code? It should be p1[p1.length() - 1] != sep instead of p1[p1.length()] != sep. We may also consider that Windows accepts both '\\' and '/'
@Rotem: Thank you for pointing out, I fixed the length problem in my answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.