File Paths in C++

The filesystem library in C++17 provides functions to parse and construct file paths in a way that is portable between operating systems; dir/file on Linux vs. dir\file on Windows, for example.

#include <iostream>
#include <filesystem>
#include <string>

namespace fs = std::filesystem;

int main() {
    // path::preferred_separator should be used to construct paths in a
    // portable way. We can use the / operator to concatenate path components.
    fs::path p = "dir1" / "dir2" / "filename";
    std::cout << "p: " << p << std::endl;

    // You should always use the / operator instead of
    // concatenating separators manually. In addition
    // to providing portability, it will also
    // normalize paths by removing superfluous separators
    // and directory changes.
    std::cout << "dir1//" / "filename" << std::endl;
    std::cout << "dir1/../dir1" / "filename" << std::endl;

    // parent_path() and filename() can be used to split a path to the
    // directory and the file. 
    std::cout << "parent_path(p): " << p.parent_path() << std::endl;
    std::cout << "filename(p): " << p.filename() << std::endl;

    // We can check whether a path is absolute.
    std::cout << fs::path("dir/file").is_absolute() << std::endl;
    std::cout << fs::path("/dir/file").is_absolute() << std::endl;

    std::string filename = "config.json";

    // Some file names have extensions following a dot. We
    // can get the extension of such names with extension().
    std::cout << fs::path(filename).extension() << std::endl;

    // To find the file's name with the extension removed,
    // use stem().
    std::cout << fs::path(filename).stem() << std::endl;

    // lexically_relative() finds a relative path between a base and a
    // target. It returns an empty path if the target cannot
    // be made relative to base.
    std::cout << fs::path("a/b/t/file").lexically_relative("a/b") << std::endl;
    std::cout << fs::path("a/c/t/file").lexically_relative("a/b") << std::endl;

    return 0;
}

To run the program, compile the code and use the resulting executable.

$ g++ -std=c++17 file_paths.cpp -o file_paths
$ ./file_paths
p: "dir1/dir2/filename"
"dir1/filename"
"dir1/filename"
parent_path(p): "dir1/dir2"
filename(p): "filename"
0
1
".json"
"config"
"t/file"
"../../c/t/file"

Note that the output might vary slightly depending on the operating system due to different path representations.

In C++, we use the filesystem library which provides similar functionality to Go’s filepath package. The fs::path class is used to represent and manipulate file paths. The / operator is overloaded to join path components, similar to filepath.Join in Go.

Methods like parent_path(), filename(), is_absolute(), extension(), and stem() correspond to Go’s Dir(), Base(), IsAbs(), Ext(), and the combination of Base() and removing the extension.

The lexically_relative() method in C++ is similar to Go’s Rel() function, finding the relative path between two paths.

Remember to compile with C++17 or later to use the filesystem library, and link against the appropriate library (you might need to add -lstdc++fs to your compile command on some systems).