In this section, we will play around with absolute and relative paths in order to see the strengths of the path class and the helper functions around it.
- First, we include all the necessary headers and declare that we use namespace std and sfilesystem.
#include <iostream>
#include <filesystem>
using namespace std;
using namespace filesystem;
- Then, we declare an example path. At this point, it is not important that the text file it refers to really exists. There are some functions, however, that throw exceptions if the underlying file does not exist.
int main()
{
path p {"testdir/foobar.txt"};
- We will have a look at four different filesystem library functions now. current_path returns us the path the program is currently executed in, the working directory. absolute accepts a relative path like our path p and returns the absolute, nonambiguous path in the whole filesystem. system_complete does practically the same as absolute on Linux, MacOS, or UNIX-like operating systems. On Windows, we would get the absolute path additionally prepended by the disk volume letter (for example, "C:"). canonical does again the same as absolute does, but then additionally removes any "." (short for "this directory") or ".." (short for "one directory up") indirections. We will play with such indirections in the following steps:
cout << "current_path : " << current_path()
<< "nabsolute_path : " << absolute(p)
<< "nsystem_complete : " << system_complete(p)
<< "ncanonical(p) : " << canonical(p)
<< 'n';
- Another nice thing about the path class is that it overloads the / operator. This way we can concatenate folder names and filenames using / and compose paths from that. Let's try it out and print a composed path.
cout << path{"testdir"} / "foobar.txt" << 'n';
- Let's play with canonical and composed paths. By giving canonical a relative path such as "foobar.txt" and a composed absolute path current_path() / "testdir", it should return us the existing absolute path. In another call, we give it our path p (which is "testdir/foobar.txt") and provide it an absolute path that is current_path(), which directs us into "testdir" and up again. This should be the same as current_path(), because of the indirection. In both calls, canonical should return us the same absolute path.
cout << "canonical testdir : "
<< canonical("foobar.txt",
current_path() / "testdir")
<< "ncanonical testdir 2 : "
<< canonical(p, current_path() / "testdir/..")
<< 'n';
- We can also test for the equivalence of two paths that are not canonical. equivalence canonicalizes the paths, which it accepts as arguments and returns true if they describe the same path after all. For this test, the path must really exist, otherwise, it throws an exception.
cout << "equivalence: "
<< equivalent("testdir/foobar.txt",
"testdir/../testdir/foobar.txt")
<< 'n';
}
- Compiling and running the program yields the following output. current_path() returns the home folder on my laptop because I executed the application from there. Our relative path p has been prepended with this directory by absolute_path, system_complete, and canonical. We see that absolute_path and system_complete yield exactly the same path on my system because it is a Mac (it would be the same on Linux). On a Windows machine, system_complete would have prepended "C:", or whatever drive the working directory is located in.
$ ./canonical_filepath
current_path : "/Users/tfc"
absolute_path : "/Users/tfc/testdir/foobar.txt"
system_complete : "/Users/tfc/testdir/foobar.txt"
canonical(p) : "/Users/tfc/testdir/foobar.txt"
"testdir/foobar.txt"
canonical testdir : "/Users/tfc/testdir/foobar.txt"
canonical testdir 2 : "/Users/tfc/testdir/foobar.txt"
equivalence: 1
- We do not handle any exceptions in our short program. If we remove the foobar.txt file in the testdir directory, then the program aborts its execution due to an exception. The canonical function requires the path to exist. There is also a weakly_canonical function that does not come with this requirement.
$ ./canonial_filepath
current_path : "/Users/tfc"
absolute_path : "/Users/tfc/testdir/foobar.txt"
system_complete : "/Users/tfc/testdir/foobar.txt"
terminate called after throwing an instance of
'std::filesystem::v1::__cxx11::filesystem_error'
what(): filesystem error: cannot canonicalize:
No such file or directory [testdir/foobar.txt] [/Users/tfc]