Skip to content

Filesystem

[Accessing file informations] – [Working with directories] – [References]

C++17 added support for filesystem features:

  • std::filesystem::path
    represents a path in a filesystem, allows access to various aspects (extension, parent_path, root_directory,…), supports concatenating paths with “/”
  • std::filesystem::(recursive_)directory_iterator
    iterates over files and subdirectories of a given directory
  • free functions for typical checks and operations
    exists, is_regular_file, is_directory, file_size, create_directories, copy_file, remove, rename, absolute (= full path), current_path ( =current working dir)

Accessing file informations

Example for simple file access:
#include <filesystem>
namespace fs = std::filesystem;
...
const std::string SOME_FILE_PATH = R"(C:\UserData\Gerald\Temp\SomeSrcDir\SomeFile.txt)";
fs::path filePath(SOME_FILE_PATH);

if (fs::exists(filePath) && fs::is_regular_file(filePath))
{
    DisplayFileInfo(filePath);
}

Output:

File "C:\\UserData\\Gerald\\Temp\\SomeSrcDir\\SomeFile.txt"
file_size      : 6306 Bytes
last write     : 13.07.2023 12:35:05.073
permissions    : r-xr-xr-x
filename       : "SomeFile.txt"
stem           : "SomeFile"
extension      : ".txt"
parent_path    : "C:\\UserData\\Gerald\\Temp\\SomeSrcDir"
relative_path  : "UserData\\Gerald\\Temp\\SomeSrcDir\\SomeFile.txt"
root_path      : "C:\\"
root_name      : "C:"
root_directory : "\\"

How to get info about last write time:

#include <windows.h> // FILETIME, used to convert to local time

std::string GetLastWriteTime(const fs::path& in_path)
{
	std::filesystem::file_time_type ftime = std::filesystem::last_write_time(in_path);
	// Check: Perhaps now a simpler conversion to local time is available within C++?

	FILETIME ft;
	memcpy(&ft, &ftime, sizeof(FILETIME));
	FILETIME ftLocal;
	if (FileTimeToLocalFileTime(&ft, &ftLocal))
	{
		SYSTEMTIME  stSystemTime;
		if (FileTimeToSystemTime(&ftLocal, &stSystemTime))
		{
			return std::format("{:0>2}.{:0>2}.{} {:0>2}:{:0>2}:{:0>2}.{:0>3}",
				stSystemTime.wDay, stSystemTime.wMonth, stSystemTime.wYear,
				stSystemTime.wHour, stSystemTime.wMinute, stSystemTime.wSecond,
				stSystemTime.wMilliseconds);
		}
	}
	return "-";
}

How to get info about file attributes:

std::string GetPermissionsAsString(const fs::path& in_path)
{
	fs::perms permissions = fs::status(in_path).permissions();

	auto bit2char = [&permissions](fs::perms bit, char c)
	{
		return (permissions & bit) == fs::perms::none ? '-' : c;
	};

	std::ostringstream oss;
	oss << bit2char(fs::perms::owner_read,   'r')
	    << bit2char(fs::perms::owner_write,  'w')
	    << bit2char(fs::perms::owner_exec,   'x')
	    << bit2char(fs::perms::group_read,   'r')
	    << bit2char(fs::perms::group_write,  'w')
	    << bit2char(fs::perms::group_exec,   'x')
	    << bit2char(fs::perms::others_read,  'r')
	    << bit2char(fs::perms::others_write, 'w')
	    << bit2char(fs::perms::others_exec,  'x');
	return oss.str();
}

How to get info about file and path properties:

void DisplayFileInfo(const fs::path& in_path)
{
    std::cout << "\n\nFile " << in_path << std::endl;
    std::cout << "file_size      : " << fs::file_size(in_path) << " Bytes" << std::endl;
    std::cout << "last write     : " << GetLastWriteTime(in_path) << std::endl;
    std::cout << "permissions    : " << GetPermissionsAsString(in_path) << std::endl;
    std::cout << "filename       : " << in_path.filename() << std::endl;
    std::cout << "stem           : " << in_path.stem() << std::endl;
    std::cout << "extension      : " << in_path.extension() << std::endl;
    std::cout << "parent_path    : " << in_path.parent_path() << std::endl;
    std::cout << "relative_path  : " << in_path.relative_path() << std::endl;
    std::cout << "root_path      : " << in_path.root_path() << std::endl;
    std::cout << "root_name      : " << in_path.root_name() << std::endl;
    std::cout << "root_directory : " << in_path.root_directory() << std::endl;
}

Remark:
Currently there is no easy way to determine file write time using std::filesystem. This will be improved with C++20.
see https://developercommunity.visualstudio.com/t/stdfilesystemfile-time-type-does-not-allow-easy-co/251213

Working with directories

In the following example assume we want to copy all “.dat” files from a given directory to some target directory:

bool CopyFiles(const std::string& in_srcDirPath, const std::string& in_tgtDirPath, const std::string& in_filePattern = "")
{
    try
    {
        // Check if source directory exists
        if (!fs::exists(in_srcDirPath) || !fs::is_directory(in_srcDirPath))
        {
            std::cout << "Could not find source dir " << in_srcDirPath << std::endl;
            return false;
        }

        // Ensure that target directory exists
        if (!fs::exists(in_tgtDirPath))
        {
            if (!fs::create_directories(in_tgtDirPath))
            {
                std::cout << "Could not create target dir " << in_tgtDirPath << std::endl;
                return false;
            }
        }

        if (in_filePattern.empty())
            std::cout << "Copy files from " << in_srcDirPath << " to " << in_tgtDirPath << "..." << std::endl;
        else
            std::cout << "Copy files with pattern '" << in_filePattern << "' from " << in_srcDirPath <<
                " to " << in_tgtDirPath << "..." << std::endl;


        // Copy relevant files from source dir to target dir
        for (const auto& entry : fs::directory_iterator(in_srcDirPath))
        {
            if (fs::is_regular_file(entry.status()))
            {
                std::string filename = entry.path().filename().string();
                if (in_filePattern.empty() || filename.find(in_filePattern) != std::string::npos)
                {
                    auto targetPath = fs::path(in_tgtDirPath) /= filename;
                    std::cout << "Copying file " << filename << std::endl;
                    fs::copy_file(entry.path(), targetPath, fs::copy_options::overwrite_existing);

                    // Make copied file writable to allow next copy
                    fs::permissions(targetPath,
                        fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write,
                        fs::perm_options::add);
                }
            }
        }
    }
    catch (std::exception& e)
    {
        std::cout << "ERROR: CopyFiles failed with: " << e.what() << std::endl;
        return false;
    }
    return true;
}

Output:

Copy files with pattern '.dat' from C:\UserData\Gerald\Temp\SomeSrcDir to C:\UserData\Gerald\Temp\SomeTgtDir...
Copying file SomeFile.dat
Copying file SomeOtherFile.dat
Copying file YetAnotherFile.ext.dat

Remarks:

  • We assume that copying of files may be executed repeatedly (e.g. to get the latest versions of some files). To make copying possible we must allow overwriting of an existing file in the target directory: copy_file is used with optional parameter copy_options::overwrite_existing
  • Within source directory the files may have readonly attributes (e.g. the file is located within some version control system). When copying to target directory these attributes remain unchanged. To be able to overwrite such a file, its file attributes have to be changed with function permissions.

References