C++递归遍历文件夹内的所有文件夹与文件的详细实现,基于VC++环境,使用Windows API(FindFirstFile和FindNextFile)递归遍历指定文件夹内的所有子文件夹和文件
C++递归遍历文件夹内的所有文件夹与文件的详细实现,基于VC++环境,使用Windows API(FindFirstFile和FindNextFile)递归遍历指定文件夹内的所有子文件夹和文件。本文提供完整源码、功能介绍、实现细节、示例用法、注意事项以及扩展建议,适用于Windows平台的C++开发。结合您之前提到的VC++开发背景(如分层窗口、UAC等),代码注重健壮性、错误处理和调试支持。
一、功能介绍
在C++开发中,遍历文件夹是常见需求,例如文件搜索、批量处理或资源管理。本程序的核心功能包括:
-
递归遍历:
-
遍历指定文件夹及其所有子文件夹,列出每个文件夹和文件的路径。
-
支持深度优先的递归方式,确保遍历所有层级。
-
-
文件和文件夹区分:
-
通过WIN32_FIND_DATA的属性区分文件夹和文件。
-
可扩展为过滤特定文件类型(如.txt)或文件夹。
-
-
错误处理:
-
处理无效路径、权限不足等异常。
-
提供日志记录,便于调试。
-
-
源码与实战:
-
提供完整VC++代码,支持Windows XP及以上版本。
-
示例展示如何遍历C:\Test目录并输出结果。
-
二、实现细节
1. Windows API简介
Windows提供FindFirstFile和FindNextFile用于遍历文件夹内容:
-
FindFirstFile:
-
原型:
cpp
HANDLE FindFirstFileW( LPCWSTR lpFileName, // 搜索路径(如L"C:\\Test\\*") LPWIN32_FIND_DATAW lpFindFileData // 文件信息 ); -
返回第一个匹配文件的句柄,填充WIN32_FIND_DATA结构。
-
-
FindNextFile:
-
继续查找下一个文件。
-
-
WIN32_FIND_DATA:
-
包含文件名(cFileName)、属性(dwFileAttributes)等。
-
FILE_ATTRIBUTE_DIRECTORY标志区分文件夹。
-
2. 递归遍历逻辑
-
使用FindFirstFile查找指定路径下的第一个文件/文件夹。
-
循环调用FindNextFile获取后续项。
-
对每个文件夹(排除.和..),递归调用遍历函数。
-
对每个文件,记录路径或执行处理。
-
关闭查找句柄(FindClose)。
3. VC++环境配置
-
头文件:<windows.h>、<string>、<iostream>。
-
字符集:建议使用Unicode(宽字符,wchar_t),支持中文路径。
-
链接库:无需额外库,Windows API由kernel32.lib提供。
-
调试工具:Visual Studio、Process Monitor(监控文件访问)。
三、完整源码
以下是VC++实现的递归遍历文件夹的完整代码,包含错误处理、日志记录和注释。
cpp
#include <windows.h>
#include <string>
#include <iostream>
#include <fstream>
// 日志记录
void LogMessage(const std::wstring& message) {
std::wofstream log("traverse_log.txt", std::ios::app);
log << L"[" << std::time(nullptr) << L"] " << message << std::endl;
}
// 递归遍历文件夹
void TraverseDirectory(const std::wstring& dirPath, int depth = 0) {
// 构造搜索路径(如L"C:\\Test\\*")
std::wstring searchPath = dirPath + L"\\*";
WIN32_FIND_DATAW findData;
HANDLE hFind = FindFirstFileW(searchPath.c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
LogMessage(L"FindFirstFile failed for " + dirPath + L": Error " + std::to_wstring(error));
return;
}
// 缩进表示层级
std::wstring indent(depth * 2, L' ');
do {
// 跳过 . 和 ..
if (wcscmp(findData.cFileName, L".") == 0 || wcscmp(findData.cFileName, L"..") == 0) {
continue;
}
// 构造完整路径
std::wstring fullPath = dirPath + L"\\" + findData.cFileName;
// 判断是文件夹还是文件
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
// 文件夹
std::wcout << indent << L"[DIR] " << fullPath << std::endl;
LogMessage(indent + L"[DIR] " + fullPath);
// 递归遍历子文件夹
TraverseDirectory(fullPath, depth + 1);
}
else {
// 文件
std::wcout << indent << L"[FILE] " << fullPath << std::endl;
LogMessage(indent + L"[FILE] " + fullPath);
}
} while (FindNextFileW(hFind, &findData));
// 检查FindNextFile失败原因
DWORD error = GetLastError();
if (error != ERROR_NO_MORE_FILES) {
LogMessage(L"FindNextFile failed for " + dirPath + L": Error " + std::to_wstring(error));
}
// 关闭查找句柄
FindClose(hFind);
}
int wmain() {
// 测试目录(替换为实际路径)
std::wstring rootDir = L"C:\\Test";
// 检查目录是否存在
DWORD attr = GetFileAttributesW(rootDir.c_str());
if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
std::wcerr << L"Invalid directory: " << rootDir << std::endl;
LogMessage(L"Invalid directory: " + rootDir);
return 1;
}
std::wcout << L"Traversing directory: " << rootDir << std::endl;
LogMessage(L"Start traversing: " + rootDir);
// 开始遍历
TraverseDirectory(rootDir);
std::wcout << L"Traversal completed." << std::endl;
LogMessage(L"Traversal completed");
return 0;
}
四、代码说明
-
TraverseDirectory:
-
核心递归函数,接受目录路径和深度参数(用于缩进显示)。
-
使用FindFirstFileW和FindNextFileW遍历目录内容。
-
排除.和..,避免无限递归。
-
对文件夹递归调用,对文件直接输出。
-
-
LogMessage:
-
记录操作日志到traverse_log.txt,包括路径、错误码等。
-
便于调试和问题定位。
-
-
错误处理:
-
检查FindFirstFile和FindNextFile的返回值。
-
记录GetLastError返回的错误码(如ERROR_ACCESS_DENIED)。
-
验证根目录有效性。
-
-
输出格式:
-
使用缩进表示层级,格式如:
[DIR] C:\Test [DIR] C:\Test\SubFolder [FILE] C:\Test\SubFolder\file.txt [FILE] C:\Test\document.pdf
-
-
运行效果:
-
遍历C:\Test及其子目录,控制台输出路径。
-
生成traverse_log.txt记录日志。
-
若路径无效或无权限,输出错误并记录。
-
-
编译与运行:
-
Visual Studio:新建Win32控制台应用,设置字符集为Unicode。
-
替换rootDir为实际路径(如C:\Test需存在)。
-
可能需管理员权限访问受限目录。
-
五、实战分析
案例背景
某C++程序需遍历项目文件夹,统计所有.cpp和.h文件,用于代码分析。但在遍历C:\Program Files时,部分子目录因权限不足失败,且未发现某些隐藏文件。
问题与解决方案
-
问题1:权限不足(ERROR_ACCESS_DENIED)
-
分析:使用Process Monitor监控文件访问,确认C:\Program Files需要管理员权限。
-
解决:
-
以管理员身份运行程序(参考UAC处理)。
-
添加Manifest请求权限(见第10篇):
xml
<requestedExecutionLevel level="requireAdministrator" />
-
-
-
问题2:未发现隐藏文件
-
分析:默认不遍历隐藏文件/文件夹。
-
解决:
-
检查dwFileAttributes的FILE_ATTRIBUTE_HIDDEN:
cpp
if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) { continue; // 跳过隐藏文件/文件夹 } -
移除此判断以包含隐藏项。
-
-
-
问题3:仅需.cpp和.h文件
-
解决:
-
添加文件扩展名过滤:
cpp
std::wstring ext = std::wstring(findData.cFileName).substr(4); std::transform(ext.begin(), ext.end(), ext.begin(), ::towlower); if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (ext == L".cpp" || ext == L".h")) { std::wcout << indent << L"[FILE] " << fullPath << std::endl; LogMessage(indent + L"[FILE] " + fullPath); }
-
-
结果
-
成功统计.cpp和.h文件,输出示例:
[DIR] C:\Project [FILE] C:\Project\main.cpp [FILE] C:\Project\utils.h [DIR] C:\Project\SubFolder [FILE] C:\Project\SubFolder\test.cpp -
日志记录权限错误和遍历路径,便于调试。
六、扩展功能
-
文件过滤:
-
支持通配符(如*.txt)或正则表达式。
-
示例:使用std::wregex匹配扩展名。
-
-
统计信息:
-
计算文件总数、大小或修改时间:
cpp
LARGE_INTEGER fileSize; fileSize.LowPart = findData.nFileSizeLow; fileSize.HighPart = findData.nFileSizeHigh; std::wcout << indent << L"[FILE] " << fullPath << L" (" << fileSize.QuadPart << L" bytes)" << std::endl;
-
-
多线程遍历:
-
将子文件夹遍历放入线程池,提升性能:
cpp
#include <thread> std::vector<std::thread> threads; if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { threads.emplace_back(TraverseDirectory, fullPath, depth + 1); } for (auto& t : threads) { t.join(); }
-
-
跨平台支持:
-
使用std::filesystem(C++17)替代Windows API:
cpp
#include <filesystem> namespace fs = std::filesystem; for (const auto& entry : fs::recursive_directory_iterator(rootDir)) { std::wcout << (entry.is_directory() ? L"[DIR] " : L"[FILE] ") << entry.path() << std::endl; }
-
七、注意事项
-
字符编码:
-
使用宽字符(wchar_t)支持中文路径。
-
确保Visual Studio项目设置Unicode字符集。
-
-
权限问题:
-
受限目录(如C:\Windows)需管理员权限。
-
参考第10篇UAC处理方案。
-
-
资源管理:
-
确保调用FindClose释放句柄,避免泄漏。
-
使用RAII封装查找句柄:
cpp
struct FindHandle { HANDLE h; FindHandle(HANDLE handle) : h(handle) {} ~FindHandle() { if (h != INVALID_HANDLE_VALUE) FindClose(h); } };
-
-
错误码:
-
常见错误:
-
ERROR_ACCESS_DENIED(5):权限不足。
-
ERROR_PATH_NOT_FOUND(3):路径无效。
-
-
使用GetLastError诊断。
-
八、资料扩展
-
MSDN文档:
-
docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfilew:FindFirstFile。
-
docs.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-win32_find_dataw:WIN32_FIND_DATA。
-
-
《Windows核心编程》:
-
第12章(文件系统):讲解文件和目录操作。
-
-
C++17 Filesystem:en.cppreference.com/w/cpp/filesystem,跨平台替代方案。
-
CodeProject:搜索“Windows file traversal”获取更多教程。
九、总结
本篇提供了VC++递归遍历文件夹的完整源码,基于Windows API实现,包含错误处理、日志记录和扩展性设计。结合实战案例,展示了如何解决权限、隐藏文件和文件过滤问题。代码适用于文件管理、资源搜索等场景,与之前的VC++主题(如UAC、分层窗口)无缝衔接。如果你有特定的遍历需求(如过滤条件、性能优化)或调试问题,请提供更多细节,我可以进一步定制代码或提供指导!
更多推荐


所有评论(0)