FindFirstFile 是 Windows API 的一部分,它用于在文件系统中查找与指定文件名(可以包含通配符,如 和 )匹配的第一个文件或目录,要找到后续匹配的文件,你需要配合使用 FindNextFile 函数,当查找完成后,必须调用 FindClose 来释放资源。
函数原型与所需头文件
在使用之前,你需要包含以下头文件:
#include <windows.h> // 包含 FindFirstFile, FindNextFile, FindClose 等 #include <tchar.h> // 包含 TCHAR 及相关字符串处理函数 #include <stdio.h> // 用于标准输入输出,如 printf
函数原型:
HANDLE FindFirstFile( [in] LPCTSTR lpFileName, [out] LPWIN32_FIND_DATA lpFindFileData );
参数说明:
lpFileName(输入): 一个指向以空结尾的字符串的指针,用于指定要查找的文件名。- 可以包含通配符 (匹配零个或多个字符) 和 (匹配单个字符)。
- 可以包含相对路径或绝对路径。
C:\\MyData\\*.txt(查找 C 盘 MyData 目录下所有 .txt 文件)*.c(在当前工作目录下查找所有 .c 文件)..\\..\\images\\*.png(查找上级目录的 images 文件夹中的所有 png 文件)
lpFindFileData(输出): 一个指向WIN32_FIND_DATA结构体指针,如果函数成功,这个结构体将被填充找到的第一个文件或目录的详细信息。
返回值:
- 成功: 返回一个有效的搜索句柄 (
HANDLE),这个句柄将用于后续的FindNextFile调用。 - 失败: 返回
INVALID_HANDLE_VALUE,要获取具体的错误信息,可以调用GetLastError()。
核心数据结构: WIN32_FIND_DATA
这个结构体是理解 FindFirstFile 的关键,它包含了找到的文件或目录的所有元数据。
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; // 文件属性 (如只读、隐藏、目录等)
FILETIME ftCreationTime; // 创建时间
FILETIME ftLastAccessTime; // 最后访问时间
FILETIME ftLastWriteTime; // 最后修改时间
DWORD nFileSizeHigh; // 文件大小 (高位)
DWORD nFileSizeLow; // 文件大小 (低位)
DWORD dwReserved0; // 保留
DWORD dwReserved1; // 保留
TCHAR cFileName[MAX_PATH]; // 文件名或目录名
TCHAR cAlternateFileName[14]; // 8.3 格式的短文件名
} WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA;
重要成员解释:
dwFileAttributes: 文件属性,可以通过位掩码来测试,常用值有:FILE_ATTRIBUTE_DIRECTORY: 如果这是一个目录,则设置此位。FILE_ATTRIBUTE_ARCHIVE: 存档文件。FILE_ATTRIBUTE_HIDDEN: 隐藏文件。FILE_ATTRIBUTE_READONLY: 只读文件。
nFileSizeHigh和nFileSizeLow: 由于文件大小可能超过 32 位DWORD的最大值,所以用两个DWORD组合成一个 64 位的整数,要获取完整的文件大小,你需要将它们合并。cFileName: 找到的文件或目录的名称。
完整示例代码
下面是一个完整的示例,演示如何查找当前目录下所有的 .c 文件,并打印它们的名称、大小和属性。
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
int _tmain(int argc, _TCHAR* argv[]) {
// 1. 准备要查找的文件名
// _T("*.c") 是一个通配符,表示查找所有以 .c 结尾的文件
// TCHAR 类型支持 Unicode 和 ANSI,更具可移植性
TCHAR* szFile = _T("*.c");
// 2. 定义 WIN32_FIND_DATA 结构体变量
WIN32_FIND_DATA FindFileData;
// 3. 调用 FindFirstFile 开始查找
// INVALID_HANDLE_VALUE 是一个宏,表示无效句柄
HANDLE hFind = FindFirstFile(szFile, &FindFileData);
// 4. 检查查找是否成功
if (hFind == INVALID_HANDLE_VALUE) {
// 如果失败,打印错误信息
_tprintf(_T("FindFirstFile failed (%d)\n"), GetLastError());
return 1;
}
// 5. 循环遍历所有匹配的文件
// FindFirstFile 找到第一个匹配项后,循环体内部用 FindNextFile 找后续的
_tprintf(_T("Found files matching '%s':\n\n"), szFile);
do {
// 打印文件名
_tprintf(_T("File Name: %s\n"), FindFileData.cFileName);
// 判断是文件还是目录
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
_tprintf(_T(" Type: Directory\n"));
} else {
_tprintf(_T(" Type: File\n"));
// 计算并打印文件大小 (合并高32位和低32位)
LARGE_INTEGER fileSize;
fileSize.LowPart = FindFileData.nFileSizeLow;
fileSize.HighPart = FindFileData.nFileSizeHigh;
_tprintf(_T(" Size: %lld bytes\n"), fileSize.QuadPart);
}
// 打印文件属性 (简化版)
_tprintf(_T(" Attributes: "));
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) _tprintf(_T("Archive "));
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) _tprintf(_T("Hidden "));
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) _tprintf(_T("ReadOnly "));
_tprintf(_T("\n\n"));
} while (FindNextFile(hFind, &FindFileData) != 0); // 只要 FindNextFile 返回非0,就继续循环
// 6. 检查循环是否因为找到所有文件而正常结束,还是因为发生错误
// FindNextFile 失败,需要用 GetLastError 判断原因
if (GetLastError() != ERROR_NO_MORE_FILES) {
_tprintf(_T("FindNextFile failed (%d)\n"), GetLastError());
FindClose(hFind); // 发生错误也要关闭句柄
return 1;
}
// 7. 查找完成,关闭句柄,释放资源
FindClose(hFind);
_tprintf(_T("Search completed.\n"));
return 0;
}
关键步骤总结
- 包含头文件:
#include <windows.h>是必须的。 - 构造搜索路径: 准备一个包含通配符的字符串,如
C:\\temp\\*。 - 声明结构体: 创建一个
WIN32_FIND_DATA类型的变量。 - 开始搜索: 调用
FindFirstFile,传入搜索路径和结构体地址,检查返回值是否为INVALID_HANDLE_VALUE。 - 处理结果: 如果成功,
WIN32_FIND_DATA结构体中就有了第一个文件的信息。 - 循环查找: 使用
do-while循环和FindNextFile来获取所有后续匹配的文件。FindNextFile同样需要检查返回值。 - 释放资源: 当查找结束后(无论是正常结束还是中途出错),必须调用
FindClose(hFind)来关闭搜索句柄,防止资源泄漏。
重要注意事项
- 资源释放:
FindClose非常重要,忘记调用它会导致句柄泄漏,长时间运行的程序可能会因此耗尽系统资源。 - 和 目录: 当你使用 作为搜索模式( 或
C:\\*)时,FindFirstFile会返回两个特殊的目录:- 代表当前目录。
- 代表父目录。 在你的代码中,需要明确处理这两种情况,通常选择跳过它们。
- 错误处理:
FindFirstFile和FindNextFile都可能失败,除了检查它们的返回值,调用GetLastError()可以获取更详细的错误代码,有助于调试。 - 跨平台性:
FindFirstFile是Windows 特有的 API,如果你需要编写跨平台的 C 代码(例如在 Linux 或 macOS 上也能运行),应该使用标准 C 库中的函数,如opendir,readdir,closedir(POSIX 标准)。 - 路径分隔符: 在 Windows 中,路径分隔符可以是反斜杠
\,但在 C 字符串中\是转义字符,为了方便,可以使用双反斜杠\\,或者使用正斜杠 ,现代 Windows API 也完全支持正斜杠作为路径分隔符,使用_T()宏和TCHAR类型可以更好地处理 Unicode 字符串路径。
