1. 遍历文件系统所需的工具:
1) MFC的CFile并没有对遍历文件系统的功能进行面向对象包装,因此遍历文件系统必须使用较为原始、底层的Win32 API;
2) 大致步骤是:
i. 使用::SetCurrentDirectory函数设定当前处于什么目录下(Windows中叫做文件夹);
ii. 使用::FindFirstFile获取当前文件夹下的第一个文件的“查找句柄”,注意!不是该文件的文件句柄,而是一种查找句柄,Win32使用了一种“查找结构”来支持文件的遍历;
iii. 接着反复调用::FindNextFile获取下一个文件的“查找句柄”,其中可以判断该文件是否为文件夹,如果是文件夹的话还可以递归地进入该文件夹进行遍历;
3) 在使用Find函数的时候会将查找的文件的相关信息填写到一个叫做“查找结构”的结构体中,然后可以利用该结构体中该文件的信息进行一些操作,该结构就是WIN32_FIND_DATA结构体:
struct WIN32_FIND_DATA {
DWORD dwFileAttributes; // 文件属性,比如是文件还是文件夹等
FILETIME ftCreationTime; // 文件创建时间
FILETIME ftLastAccessTime; // 最近一次文件被访问的时间(包括读写)
FILETIME ftLastWriteTime; // 最近一次文件被写的时间
CHAR cFileName[MAX_PATH]; // 文件的完整绝对路径
...
};
!要判断查找的文件是文件还是文件夹只需要看一下dwFileAttributes的位掩码FILE_ATTRIBUTE_DIRECTORY是否为1就行了
4) FindFirstFile:
i. HANDLE ::FindFirstFile(LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData);
ii. 第一个参数是目标文件名,可以使用通配符,一般在遍历的时候查找的第一个文件都用_T("*.*")来表示;
iii. 第二个参数就是上面所说的WIN32_FIND_DATA结构的指针,查找到的文件的相关信息就保存在该结构体中;
iv. 返回值就是相关的“查找句柄”,而不是文件句柄hFile,也不是WIN32_FIND_DATA的句柄,而是Win32 API内部维护的一个查找体的句柄,如果失败则返回INVALID_HANDLE_VALUE;
5) FindNextFile:
i. BOOL ::FindNextFile(HANDLE hFindFile, LPWIN32_FIND_DATA lpFindData);
ii. 第一个参数就是上一次查找得到的查找句柄,通常在调用FindFirstFile之后将返回值传给该参数进行迭代查找,查找的文件的信息同样放在lpFindData中;
iii. 调用成功返回TRUE,否则返回FALSE;
6) SetCurrentDirectory:
i. BOOL ::SetCurrentDirectory(LPCTSTR lpPathName);
ii. 将当前所在目录设成lpPathName所指定的目录,可以是绝对路径也可以是相对路径;
iii. 有时可能提供的路径有问题,因此调用失败返回FALSE,成功则为TRUE;
7) GetCurrentDirectory:获取当前路径
i. BOOL ::GetCurrentDirectory(DWORD nBufferLength, LPSTR lpBuffer);
ii. 第一个参数是缓冲区能接受的字符个数,注意!是字符个数而不是字节数,这最主要是兼容Unicode;
iii. lpBuffer就是缓冲区;
iv. 为了兼容Unicode最好这样调用:::GetCurrentDirectory(sizeof(szPath) / sizeof(TCHAR), szPath);
2. 示例——递归遍历一个文件夹:
void trace_t(int nCount) // 在一行的开头打nCount个制表符以示文件夹的层级
{
while (nCount)
{
TRACE(_T("\t"));
nCount--;
}
}
void EnumerateFile(int nLevel) // 递归遍历文件系统
{
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(_T("*.*"), &fd); // 开始从任意位置查找
if (hFind != INVALID_HANDLE_VALUE) // 注意FindFirstFile的返回值是HANDLE
{
do
{
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
CString name = fd.cFileName;
if (name != _T(".") && name != _T(".."))
{
trace_t(nLevel); // 先打印表示所在层级的制表符
TRACE(_T("[D]%s\n"), fd.cFileName); // 文件夹用[D]来表示
::SetCurrentDirectory(fd.cFileName); // 进入刚查找的文件夹
EnumerateFile(nLevel + 1); // 如果是文件夹则递归往下遍历
::SetCurrentDirectory(_T("..")); // 遍历完后返回上层文件夹中继续遍历
}
}
else
{
CString name = fd.cFileName;
trace_t(nLevel);
TRACE(_T("[F]%s\n"), fd.cFileName); // 普通文件用[F]来表示
}
} while (::FindNextFile(hFind, &fd)); // 继续查找,注意FindNextFile返回值是BOOL
::FindClose(hFind);
}
}
void CMainWindow::OnLButtonDown(UINT nFlags, CPoint point)
{
::SetCurrentDirectory(_T("C:\\Proj")); // 设置遍历目标文件夹
EnumerateFile(0); // 刚开始时层级是0,即第一层中的文件名前面没有制表符
}
结果如下:
[D]mfc
[D]code
[F].Hello.h.swp
[F]Hello.cpp
[F]Hello.h
[D]p1
[D]Hello
[D]Debug
[F]Hello.exe
[F]Hello.ilk
[F]Hello.obj
[F]Hello.pch
[F]Hello.pdb
[F]vc60.idb
[F]vc60.pdb
[F]Hello.cpp
[F]Hello.dsp
[F]Hello.dsw
[F]Hello.h
[F]Hello.ncb
[F]Hello.opt
[F]Hello.plg
[D]Shapes
[F]ChildView.cpp
[F]ChildView.h
[D]Debug
[F]MainFrm.cpp
[F]MainFrm.h
[F]ReadMe.txt
[D]res
[F]Shapes.ico
[F]Shapes.rc2
[F]Resource.h
[F]Shapes.aps
[F]Shapes.clw
[F]Shapes.cpp
[F]Shapes.dsp
[F]Shapes.dsw
[F]Shapes.h
[F]Shapes.ncb
[F]Shapes.opt
[F]Shapes.rc
[F]StdAfx.cpp
[F]StdAfx.h
[D]Test
[D]Debug
[F]Test.exe
[F]Test.ilk
[F]Test.obj
[F]Test.pch
[F]Test.pdb
[F]vc60.idb
[F]vc60.pdb
[F]Test.cpp
[F]Test.dsp
[F]Test.dsw
[F]Test.h
[F]Test.ncb
[F]Test.opt
[F]Test.plg
[D]TicTac
[D]Debug
[F]TicTac.exe
[F]TicTac.ilk
[F]TicTac.obj
[F]TicTac.pch
[F]TicTac.pdb
[F]vc60.idb
[F]vc60.pdb
[F]TicTac.cpp
[F]TicTac.dsp
[F]TicTac.dsw
[F]TicTac.h
[F]TicTac.ncb
[F]TicTac.opt
[F]TicTac.plg
[D]p3
[D]TicTac
[D]Debug
[F]TicTac.exe
[F]TicTac.ilk
[F]TicTac.obj
[F]TicTac.pch
[F]TicTac.pdb
[F]vc60.idb
[F]vc60.pdb
[F]TicTac.cpp
[F]TicTac.dsp
[F]TicTac.dsw
[F]TicTac.h
[F]TicTac.ncb
[F]TicTac.opt
[F]TicTac.plg
[D]TT
[D]Debug
可以清楚的看到层级关系!