The Boost C++ Libraries

Paths

boost::filesystem::path is the central class in Boost.Filesystem for representing and processing paths. Definitions can be found in the namespace boost::filesystem and in the header file boost/filesystem.hpp. Paths can be built by passing a string to the constructor of boost::filesystem::path (see Example 35.1).

Example 35.1. Using boost::filesystem::path
#include <boost/filesystem.hpp>

using namespace boost::filesystem;

int main()
{
  path p1{"C:\\"};
  path p2{"C:\\Windows"};
  path p3{L"C:\\Boost C++ \u5E93"};
}

boost::filesystem::path can be initialized with wide strings. Wide strings are interpreted as Unicode and make it easy to create paths using characters from nearly any language. This is a crucial difference from Boost.Filesystem 2, which provided separate classes, such as boost::filesystem::path and boost::filesystem::wpath, for different string types.

Please note that Boost.Filesystem doesn’t support std::u16string or std::u32string. Your compiler aborts with an error if you try to initialize boost::filesystem::path with one of these string types.

None of the constructors of boost::filesystem::path validate paths or check whether the given file or directory exists. Thus, boost::filesystem::path can be instantiated even with meaningless paths.

Example 35.2. Meaningless paths with boost::filesystem::path
#include <boost/filesystem.hpp>

using namespace boost::filesystem;

int main()
{
  path p1{"..."};
  path p2{"\\"};
  path p3{"@:"};
}

Example 35.2 runs without any problems because paths are just strings. boost::filesystem::path only processes strings; the file system is not accessed.

Because boost::filesystem::path processes strings, the class provides several member functions to retrieve a path as a string.

In general, Boost.Filesystem differentiates between native paths and generic paths. Native paths are operating system specific and must be used when calling operating system functions. Generic paths are portable and independent of the operating system.

Example 35.3. Retrieving paths from boost::filesystem::path as strings
#include <boost/filesystem.hpp>
#include <iostream>

using namespace boost::filesystem;

int main()
{
  path p{"C:\\Windows\\System"};

#ifdef BOOST_WINDOWS_API
  std::wcout << p.native() << '\n';
#else
  std::cout << p.native() << '\n';
#endif

  std::cout << p.string() << '\n';
  std::wcout << p.wstring() << '\n';

  std::cout << p.generic_string() << '\n';
  std::wcout << p.generic_wstring() << '\n';
}

The member functions native(), string() and wstring() all return paths in the native format. When run on Windows, Example 35.3 writes C:\Windows\System to the standard output stream three times.

The member functions generic_string() and generic_wstring() both return paths in a generic format. These are portable paths; the string is normalized based on rules of the POSIX standard. Generic paths are therefore identical to paths used on Linux. For example, the slash is used as the separator for directories. If Example 35.3 is run on Windows, both generic_string() and generic_wstring() write C:/Windows/System to the standard output stream.

The return value of member functions returning native paths depends on the operating system the program is executed on. The return value of member functions returning generic paths is independent of the operating system. Generic paths uniquely identify files and directories independently from the operating system and therefore make it easy to write platform-independent code.

Because boost::filesystem::path can be initialized with different string types, several member functions are provided to retrieve paths in different string types. While string() and generic_string() return a string of type std::string, wstring() and generic_wstring() return a string of type std::wstring.

The return type of native() depends on the operating system the program was compiled for. On Windows a string of type std::wstring is returned. On Linux it is a string of type std::string.

The constructor of boost::filesystem::path supports both generic and platform-dependent paths. In Example 35.3, the path C:\\Windows\\System is Windows specific and not portable. Notice also that because the backslash is an escape character in C++, it must be escaped itself. This path will only be recognized correctly by Boost.Filesystem if the program is run on Windows. When executed on a POSIX compliant operating system such as Linux, this example will return C:\Windows\System for all member functions called. Since the backslash is not used as a separator on Linux in either the portable or the native format, Boost.Filesystem does not recognize this character as a separator for files and directories.

The macro BOOST_WINDOWS_API comes from Boost.System and is defined if the example is compiled on Windows. The respective macro for POSIX operating systems is called BOOST_POSIX_API.

Example 35.4 uses a portable path to initialize boost::filesystem::path.

Example 35.4. Initializing boost::filesystem::path with a portable path
#include <boost/filesystem.hpp>
#include <iostream>

using namespace boost::filesystem;

int main()
{
  path p{"/"};
  std::cout << p.string() << '\n';
  std::cout << p.generic_string() << '\n';
}

Because generic_string() returns a portable path, its value will be a slash (/), the same as was used to initialize boost::filesystem::path. However, the member function string() returns different values depending on the platform. On Windows and Linux it returns /. The output is the same because Windows accepts the slash as a directory separator even though it prefers the backslash.

boost::filesystem::path provides several member functions to access certain components of a path.

Example 35.5. Accessing components of a path
#include <boost/filesystem.hpp>
#include <iostream>

using namespace boost::filesystem;

int main()
{
  path p{"C:\\Windows\\System"};
  std::cout << p.root_name() << '\n';
  std::cout << p.root_directory() << '\n';
  std::cout << p.root_path() << '\n';
  std::cout << p.relative_path() << '\n';
  std::cout << p.parent_path() << '\n';
  std::cout << p.filename() << '\n';
}

If Example 35.5 is executed on Windows, the string C:\\Windows\\System is interpreted as a platform-dependent path. Consequently, root_name() returns "C:", root_directory() returns "\", root_path() returns "C:\", relative_path() returns "Windows\System", parent_path() returns "C:\Windows", and filename() returns "System".

All member functions return platform-dependent paths because boost::filesystem::path stores paths in a platform-dependent format internally. To retrieve paths in a portable format, member functions such as generic_string() need to be called explicitly.

If Example 35.5 is executed on Linux, the returned values are different. Most of the member functions return an empty string, except relative_path() and filename(), which return "C:\Windows\System". This means that the string C:\\Windows\\System is interpreted as a file name on Linux, which is understandable given that it is neither a portable encoding of a path nor a platform-dependent encoding on Linux. Therefore, Boost.Filesystem has no choice but to interpret it as a file name.

Boost.Filesystem provides additional member functions to verify whether a path contains a specific substring. These member functions are: has_root_name(), has_root_directory(), has_root_path(), has_relative_path(), has_parent_path(), and has_filename(). Each member function returns a value of type bool.

There are two more member functions that can split a file name into its components. They should only be called if has_filename() returns true. Otherwise, they will return empty strings because there is nothing to split if there is no file name.

Example 35.6. Receiving file name and file extension
#include <boost/filesystem.hpp>
#include <iostream>

using namespace boost::filesystem;

int main()
{
  path p{"photo.jpg"};
  std::cout << p.stem() << '\n';
  std::cout << p.extension() << '\n';
}

Example 35.6 returns "photo" for stem() and ".jpg" for extension().

Instead of accessing the components of a path via member function calls, you can also iterate over the components.

Example 35.7. Iterating over components of a path
#include <boost/filesystem.hpp>
#include <iostream>

using namespace boost::filesystem;

int main()
{
  path p{"C:\\Windows\\System"};
  for (const path &pp : p)
    std::cout << pp << '\n';
}

If executed on Windows, Example 35.7 will successively output "C:", "/", "Windows" and "System". On Linux, the output will be "C:\Windows\System".

While the previous examples introduced various member functions to access different components of a path, Example 35.8 uses a member function to modify a path.

Example 35.8. Concatenating paths with operator/=
#include <boost/filesystem.hpp>
#include <iostream>

using namespace boost::filesystem;

int main()
{
  path p{"C:\\"};
  p /= "Windows\\System";
  std::cout << p.string() << '\n';
}

Using operator/=, Example 35.8 appends one path to another. On Windows, the program outputs C:\Windows\System. On Linux, the output is C:\/Windows\System, since the slash is used as a separator for files and directories. The slash is also the reason why operator/= has been overloaded; after all, the slash is part of the operator.

Besides operator/=, Boost.Filesystem provides the member functions remove_filename(), replace_extension(), make_absolute(), and make_preferred() to modify paths. The last member function mentioned is particularly designed for use on Windows.

Example 35.9. Preferred notation with make_preferred()
#include <boost/filesystem.hpp>
#include <iostream>

using namespace boost::filesystem;

int main()
{
  path p{"C:/Windows/System"};
  std::cout << p.make_preferred() << '\n';
}

Even though the backslash is used as the separator for files and directories by default, Windows still accepts the slash. C:/Windows/System is therefore a valid native path. With make_preferred() such a path can be converted to the preferred notation on Windows. Example 35.9 displays "C:\Windows\System".

The member function make_preferred() has no effect on POSIX compliant operating systems such as Linux.

Please note that make_preferred() not only returns the converted path, but also modifies the object it has been called on. p contains C:\Windows\System after the call.