Windows API 提供 FindFirstFile / FindNextFile / FindClose 三件套實現目錄枚舉。關鍵結構 WIN32_FIND_DATA 包含文件名、屬性、時間戳、大小等信息。
#include
#include
#include
#include
void TraverseDirectory(const std::string& rootPath, std::vector& files, std::vector& dirs)
{
// 構建搜索模式:目錄路徑 + "\\*"
std::string searchPath = rootPath;
if (searchPath.back() != '\\') searchPath += '\\';
searchPath += '*';
WIN32_FIND_DATAA findData;
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
if (err != ERROR_FILE_NOT_FOUND)
std::cerr << "FindFirstFile failed: " << err << " for " << searchPath << std::endl;
return;
}
do
{
// 跳過 "." 和 ".."
if (strcmp(findData.cFileName, ".") == 0 || strcmp(findData.cFileName, "..") == 0)
continue;
std::string fullPath = rootPath + "\\" + findData.cFileName;
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// 子目錄:記錄並遞歸
dirs.push_back(fullPath);
TraverseDirectory(fullPath, files, dirs);
}
else
{
// 文件:記錄
files.push_back(fullPath);
}
}
while (FindNextFileA(hFind, &findData));
DWORD err = GetLastError();
FindClose(hFind);
if (err != ERROR_NO_MORE_FILES)
std::cerr << "FindNextFile error: " << err << std::endl;
}
// 使用示例
int main()
{
std::vector files, dirs;
TraverseDirectory("C:\\MyProject", files, dirs);
std::cout << "Directories (" << dirs.size() << "):\n";
for (auto& d : dirs) std::cout << " DIR: " << d << "\n";
std::cout << "\nFiles (" << files.size() << "):\n";
for (auto& f : files) std::cout << " FILE: " << f << "\n";
return 0;
}
void TraverseDirectoryMFC(const CString& strRoot, CStringArray& arrFiles, CStringArray& arrDirs)
{
CString strFind = strRoot;
if (strFind.Right(1) != _T("\\")) strFind += _T("\\");
strFind += _T("*.*");
WIN32_FIND_DATA findData;
HANDLE hFind = FindFirstFile(strFind, &findData);
if (hFind == INVALID_HANDLE_VALUE) return;
do
{
if (_tcscmp(findData.cFileName, _T(".")) == 0 || _tcscmp(findData.cFileName, _T("..")) == 0)
continue;
CString strFull = strRoot + _T("\\") + findData.cFileName;
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
arrDirs.Add(strFull);
TraverseDirectoryMFC(strFull, arrFiles, arrDirs);
}
else
{
arrFiles.Add(strFull);
}
}
while (FindNextFile(hFind, &findData));
FindClose(hFind);
}
Windows 默認限制 MAX_PATH (260)。解決方案:
\\\\?\\,如 \\\\?\\C:\\VeryLongPathFindFirstFileW)配合 \\\\?\\ 前綴FILE_ATTRIBUTE_REPARSE_POINT 標識重解析點std::set/CMap)去重GetFinalPathNameByHandle 獲取真實路徑比對FindFirstFile 失敗可能因權限不足(ERROR_ACCESS_DENIED),應捕獲繼續而非中止vector::reserve 或使用 CStringArrayWIN32_FIND_DATA 已包含,無需額外 GetFileAttributes
void TraverseWithCFileFind(const CString& root, CStringArray& files, CStringArray& dirs)
{
CFileFind finder;
CString strWild = root + _T("\\*.*");
BOOL bFound = finder.FindFile(strWild);
while (bFound)
{
bFound = finder.FindNextFile();
if (finder.IsDots()) continue;
CString full = finder.GetFilePath();
if (finder.IsDirectory())
{
dirs.Add(full);
TraverseWithCFileFind(full, files, dirs);
}
else
{
files.Add(full);
}
}
finder.Close();
}
CFileFind 封裝了 Unicode/ANSI、錯誤處理,代碼更簡潔但性能略低於原生 API。
| 場景 | 關鍵邏輯 |
|---|---|
| 文件搜索工具 | 遍歷時匹配文件名/內容(StrStrI/正則/全文索引) |
| 磁盤空間分析 | 累加文件大小(findData.nFileSizeLow/High),按目錄/類型統計 |
| 批量重命名/修改屬性 | 遍歷收集列表 → 用戶確認 → 批量 MoveFile/SetFileAttributes |
| 備份/同步工具 | 對比源目標 FILETIME/大小/哈希,增量複製 |
| 惡意軟件掃描 | 計算文件哈希(MD5/SHA256)匹配特徵庫 |
Visual Studio 2017 15.7+ 完整支持 <filesystem>:
#include
#include
namespace fs = std::filesystem;
void ModernTraverse(const fs::path& root)
{
try
{
for (auto& entry : fs::recursive_directory_iterator(root,
fs::directory_options::skip_permission_denied))
{
if (entry.is_directory())
std::cout << "DIR: " << entry.path() << "\n";
else
std::cout << "FILE: " << entry.path() << " (" << entry.file_size() << " bytes)\n";
}
}
catch (const fs::filesystem_error& e)
{
std::cerr << "FS Error: " << e.what() << "\n";
}
}
優勢:跨平台、異常安全、範圍-based for 循環、自動處理符號鏈接循環、支持 UTF-8 路徑。建議新項目優先使用。
目錄遞歸遍歷是系統級編程基礎技能。掌握 FindFirstFile/FindNextFile 底層 API 細節(屬性判斷、錯誤碼處理、長路徑、重解析點),並了解 MFC CFileFind 封裝與現代 std::filesystem 替代方案,可根據項目約束(C++ 版本、跨平台需求、性能敏感度)選擇最佳實現。