Hi I want to get the full file path of a given filename Ex. string a = "sample.txt" and it will basically search for any sample.txt on drive c:\ and if it was found, I want to output the fullpath ex. C:\Users\windows10\desktop\sample.txt
any thought on how to do that?
Note: I highly appreciate it there's no boost::filesystem involved
CodePudding user response:
In C 17 and above you can use std::filesystem
in a OS independent way, which is better than just aiming for Windows (imo).
It can be done in many ways, but I have include a solution that utilise recursion and looks at the file path. As you can see, I base it on string comparison, so if you want to it can be adopted to using partial name matching using string contains in C 23.
#include <string>
#include <vector>
#include <filesystem>
#include <iostream>
std::vector<std::string> get_file_paths(const std::string dir, const std::string& file_name)
{
std::cout << "Looking for files in: " << dir << "\n";
std::vector<std::string> files;
for (auto const& dir_entry : std::filesystem::directory_iterator(std::filesystem::path(dir)))
{
if (std::filesystem::is_directory(dir_entry.path()))
{
auto sub_files = get_file_paths(dir_entry.path().string(), file_name);
files.insert(files.end(), sub_files.begin(), sub_files.end());
}
else
{
if (dir_entry.path().filename() == file_name)
{
files.emplace_back(dir_entry.path().string());
}
}
}
return files;
}
int main(int argc, char *argv[])
{
(void) argc;
auto start_directory = std::string(argv[1]);
auto file_name = std::string(argv[2]);
std::cout << "Finding files with name " << file_name << " starting from directory " << start_directory << "\n";
auto files = get_file_paths(start_directory, file_name);
std::cout << "\n\nFound files: \n";
for (const auto& file : files)
{
std::cout << file << "\n";
}
}
CodePudding user response:
For Win32, you'd do this operation with recursion and the FindFirstFileExW
/ FindNextFileW
operations.
This sample code is for taking paths with wildcards and finding the path of the matches.
#include <Windows.h>
#include <cstdlib>
#include <cwchar>
#include <list>
#include <memory>
#include <string>
namespace
{
inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }
struct find_closer { void operator()(HANDLE h) noexcept { assert(h != INVALID_HANDLE_VALUE); if (h) FindClose(h); } };
using ScopedFindHandle = std::unique_ptr<void, find_closer>;
}
void SearchForFiles(const wchar_t* path, std::list<std::wstring>& files, bool recursive)
{
// Process files
WIN32_FIND_DATAW findData = {};
ScopedFindHandle hFile(safe_handle(FindFirstFileExW(path,
FindExInfoBasic, &findData,
FindExSearchNameMatch, nullptr,
FIND_FIRST_EX_LARGE_FETCH)));
if (hFile)
{
for (;;)
{
if (!(findData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY)))
{
wchar_t drive[_MAX_DRIVE] = {};
wchar_t dir[_MAX_DIR] = {};
_wsplitpath_s(path, drive, _MAX_DRIVE, dir, _MAX_DIR, nullptr, 0, nullptr, 0);
SConversion conv = {};
wchar_t fp[_MAX_PATH] = {};
_wmakepath_s(fp, drive, dir, findData.cFileName, nullptr);
files.push_back(fp);
}
if (!FindNextFileW(hFile.get(), &findData))
break;
}
}
// Process directories
if (recursive)
{
wchar_t searchDir[MAX_PATH] = {};
{
wchar_t drive[_MAX_DRIVE] = {};
wchar_t dir[_MAX_DIR] = {};
_wsplitpath_s(path, drive, _MAX_DRIVE, dir, _MAX_DIR, nullptr, 0, nullptr, 0);
_wmakepath_s(searchDir, drive, dir, L"*", nullptr);
}
hFile.reset(safe_handle(FindFirstFileExW(searchDir,
FindExInfoBasic, &findData,
FindExSearchLimitToDirectories, nullptr,
FIND_FIRST_EX_LARGE_FETCH)));
if (!hFile)
return;
for (;;)
{
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (findData.cFileName[0] != L'.')
{
wchar_t subdir[MAX_PATH] = {};
{
wchar_t drive[_MAX_DRIVE] = {};
wchar_t dir[_MAX_DIR] = {};
wchar_t fname[_MAX_FNAME] = {};
wchar_t ext[_MAX_FNAME] = {};
_wsplitpath_s(path, drive, dir, fname, ext);
wcscat_s(dir, findData.cFileName);
_wmakepath_s(subdir, drive, dir, fname, ext);
}
SearchForFiles(subdir, files, recursive);
}
}
if (!FindNextFileW(hFile.get(), &findData))
break;
}
}
}