[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.