112

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.

sas4740
  • 4,510
  • 8
  • 26
  • 23
  • Possibly distantly related: http://stackoverflow.com/questions/5772992/get-relative-path-from-two-absolute-paths (specifically, related to _that_ question is boost's `complete`) – Lightness Races in Orbit Jun 09 '11 at 18:53

8 Answers8

116

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
user16217248
  • 3,119
  • 19
  • 19
  • 37
  • 1
    This is also in TR2, which is likely to start shipping with compilers next year. – ildjarn Jun 09 '11 at 19:01
  • @ildjarn: Thanks for the permalink! I was always wondering if it exists. –  Jun 09 '11 at 19:04
  • 1
    @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 Jun 09 '11 at 19:12
  • 1
    @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 Nurk Jun 09 '11 at 19:44
  • @Fred : From what I remember, policy was put in place when 1.35 was released to prevent that from ever happening. – ildjarn Jun 09 '11 at 19:46
  • @ildjarn: Filesystem is already on v3, do you expect to stop there? Changes to the docs mean blindly re-editing to auto-updating links will be confusing (different parameters, name changes from the text in the answer, etc.), even if the links lead to a non-404 page. Filesystem v2 was the default for 1.44 and 1.45, so Filesystem has *already* undergone doc changes (after 1.35 that you mention) that could be very confusing for an answer written when those were the current versions. – Fred Nurk Jun 09 '11 at 20:01
  • 1
    @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. – ildjarn Jun 09 '11 at 20:02
  • 1
    @ildjarn: How do you predict what a given library will change in the future so you can know if all the answers you edit will make sense for all future versions? – Fred Nurk Jun 09 '11 at 20:03
  • 1
    @Fred : Are you implying that at some point in the future, Boost.FileSystem might lose the ability to do what the OP is asking for? Really? In general, the answer to your question is "I use my brain." So far that seems to have worked out just fine. – ildjarn Jun 09 '11 at 20:05
  • 2
    With C++17 use `#include `, `namespace fs = std::filesystem;`. With C++14 use `#include `, `namespace fs = std::experimental::filesystem;`. – Roi Danton Apr 07 '17 at 15:27
  • Never say only – if other answers are correct, Boost is not the only solution. – user2394284 Aug 07 '17 at 13:16
63

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;
}
user16217248
  • 3,119
  • 19
  • 19
  • 37
Shawn Blakesley
  • 1,743
  • 1
  • 17
  • 33
  • 9
    As of C++17, this was merged into the (non-experimental) header. See https://en.cppreference.com/w/cpp/filesystem. – Eli_B Aug 25 '18 at 15:23
  • 1
    Thank you so much, this answer is a way to go in case if c++ 17 is available on the project. – n0ne Mar 11 '21 at 06:45
  • 1
    in my case its just joining them directly (and not putting a `/` or ```\``` between them) – 0xB00B Apr 14 '21 at 16:10
  • @0xB00B just use `A + B`, `fs::path` override the operator `/` / `+` to append/concat. see reference https://en.cppreference.com/w/cpp/filesystem/path/concat – Kevin Chan Oct 20 '22 at 03:07
  • @Kevin Chan i was already talking about that, it wasn't working properly due to some implementation bug. – 0xB00B Oct 20 '22 at 14:23
52

Check out QDir for that:

QString path = QDir(dirPath).filePath(fileName);
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
Stephen Chu
  • 12,724
  • 1
  • 35
  • 46
10

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.

Azeem
  • 11,148
  • 4
  • 27
  • 40
frankc
  • 11,290
  • 4
  • 32
  • 49
  • 2
    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. – LoSciamano Jun 09 '11 at 19:19
7

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

QFileInfo fi( QDir("/tmp"), "file" );
QString path = fi.absoluteFilePath();
Azeem
  • 11,148
  • 4
  • 27
  • 40
LoSciamano
  • 1,099
  • 10
  • 21
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
kainjow
  • 3,955
  • 1
  • 20
  • 17
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().

Azeem
  • 11,148
  • 4
  • 27
  • 40
Frank Osterfeld
  • 24,815
  • 5
  • 58
  • 70
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;
    }
}
Honza Vojtěch
  • 685
  • 1
  • 7
  • 27
  • 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 May 10 '23 at 11:26
  • @Rotem: Thank you for pointing out, I fixed the length problem in my answer. – Honza Vojtěch May 11 '23 at 10:40