数据结构中对于树结构的节点遍历方式有深度优先和广度优先两种方法,其中广度优先也叫作层序遍历。在具体的代码实现中,深度优先算法采用的是递归算法,需要使用到栈这种数据结构;而广度优先采用的是非递归算法,需要用到队列这种数据结构;如果你之前对这两种方式有所了解的话那到这里你就已经知道我说的细节了,如果没有了解过,也没关系,可以直接看下面的代码,可以获取到更直接的感受和理解。
因为Windows目录结构的组织其实也就是一棵树,这里以对Windows目录的遍历作为Demo来演示这两种遍历方式。
第一种方式:深度优先方式(DFS)。
/**********************************
函数名称:TravelPathByDFS
功 能:深度优先遍历(DFS)指定路径
参 数:lpPath:要遍历的起始路径
IsRecursive:是否递归遍历(TRUE:递归遍历,FALSE:非递归遍历)
返 回 值:VOID
备 注:无
***********************************/
VOID TravelPathByDFS(CONST TCHAR* lpPath, BOOL IsRecursive)
{
HANDLE hFindFile = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA tagFindData = {0};
TCHAR szPath[BUFFER_SIZE] = {TEXT('\0')};
TCHAR szTemp[BUFFER_SIZE] = {TEXT('\0')};
if(NULL == lpPath || lstrlen(lpPath) > (BUFFER_SIZE - 1))
{
return;
}//end if(NULL == lpPath || lstrlen(lpPath) > (BUFFER_SIZE - 1))
memset(szPath, 0, sizeof(szPath));
lstrcpy(szPath, lpPath);
if(TEXT('\\') != szPath[lstrlen(szPath) - 1])
{
szPath[lstrlen(szPath)] = TEXT('\\');
}//end if(TEXT('\\') != szPath[lstrlen(szPath) - 1])
lstrcat(szPath, TEXT("*.*"));
if((hFindFile = ::FindFirstFile(szPath, &tagFindData)) == INVALID_HANDLE_VALUE)
{
return;
}//end if((hFindFile = ::FindFirstFile(szPath, &tagFindData)) == INVALID_HANDLE_VALUE)
do
{
//首先排除掉 "." 和 ".."
if(0 == ::lstrcmpi(tagFindData.cFileName, TEXT(".")) ||
0 == ::lstrcmpi(tagFindData.cFileName, TEXT(".."))
)
{
continue;
}
//必须要有这一步,用 szTemp 过渡,防止 szPath 被改了
memset(szTemp, 0, sizeof(szTemp));
lstrcpy(szTemp, szPath);
szTemp[lstrlen(szTemp) - lstrlen(TEXT("*.*"))] = TEXT('\0');
lstrcat(szTemp, tagFindData.cFileName);
//判断类型
if(FILE_ATTRIBUTE_DIRECTORY & tagFindData.dwFileAttributes)
{
if(TRUE == IsRecursive)
{
TravelPathByDFS(szTemp, IsRecursive); //szTemp 为需要继续遍历的路径
}//end if(TRUE == IsRecursive)
}//end if(FILE_ATTRIBUTE_DIRECTORY & tagFindData.dwFileAttributes)
else
{
//szTemp 为获取到的文件名
HandleFindFiles(szTemp); //这个函数时自定义的对遍历到的文件进行处理的函数
}//end else
}while(TRUE == ::FindNextFile(hFindFile, &tagFindData));
::FindClose(hFindFile);
}
对于这种方式,需要注意的是栈空间的使用,因为每个程序在编译的时候都有默认的栈空间大小,而递归过程对于栈的消耗比较大,如果递归过程中把栈撑爆了的话,程序就会出问题,所以,这种情况适用于递归层次不是很多的情况。
第二种方式:广度优先的方式(BFS)。
/**********************************
函数名称:TravelPathByBFS
功 能:广度度优先遍历(BFS)指定路径
参 数:lpPath:要遍历的起始路径
IsRecursive:是否递归遍历(TRUE:递归遍历,FALSE:非递归遍历)
返 回 值:VOID
备 注:无
***********************************/
VOID TravelPathByBFS(CONST TCHAR* lpPath, BOOL IsRecursive)
{
TCHAR szPath[BUFFER_SIZE] = {TEXT('\0')}; ;
if(NULL == lpPath || lstrlen(lpPath) > (BUFFER_SIZE - 1))
{
return;
}//end if(NULL == lpPath || lstrlen(lpPath) > (BUFFER_SIZE - 1))
memset(szPath, 0, sizeof(szPath));
lstrcpy(szPath, lpPath);
if(::GetFileAttributes(szPath) & FILE_ATTRIBUTE_DIRECTORY) // 目录
{
#ifndef _UNICODE
queue<std::string> queueDirectory;
#else
queue<std::wstring> queueDirectory;
#endif
if(TEXT('\\') == szPath[lstrlen(szPath) - 1])
{
szPath[lstrlen(szPath) - 1] = TEXT('\0');
}//end if(TEXT('\\') == szPath[lstrlen(szPath) - 1])
queueDirectory.push(szPath);
WIN32_FIND_DATA tagFindResult = {0};
HANDLE hHandle = INVALID_HANDLE_VALUE;
while(queueDirectory.size() > 0)
{
#ifndef _UNICODE
std::string strTempFolder = queueDirectory.front();
#else
std::wstring strTempFolder = queueDirectory.front();
#endif
strTempFolder.append(TEXT("\\*"));
if((hHandle = ::FindFirstFile(strTempFolder.c_str(), &tagFindResult)) != INVALID_HANDLE_VALUE)
{
do
{
if(tagFindResult.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if(TRUE == IsRecursive)
{
//将当前的目录添加到队列尾部
if(0 == ::lstrcmp(TEXT("."), tagFindResult.cFileName) ||
0 == ::lstrcmp(TEXT(".."), tagFindResult.cFileName))
{
continue;
}//end if
strTempFolder = queueDirectory.front();
strTempFolder.append(TEXT("\\")).append(tagFindResult.cFileName);
queueDirectory.push(strTempFolder);
}//end if(TRUE == IsRecursive)
}//end if(tagFindResult.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
else
{
//当前是文件,获取文件全路径并输出
strTempFolder = queueDirectory.front();
strTempFolder.append(TEXT("\\")).append(tagFindResult.cFileName);
HandleFindFiles((TCHAR *)strTempFolder.c_str());
}//end else
}while(::FindNextFile(hHandle, &tagFindResult));
}//end if((hHandle = ::FindFirstFile(strTempFolder.c_str(), &tagFindResult)) != INVALID_HANDLE_VALUE)
if(INVALID_HANDLE_VALUE != hHandle)
{
::FindClose(hHandle);
hHandle = INVALID_HANDLE_VALUE;
}//end if(INVALID_HANDLE_VALUE != hHandle)
//将当前已经遍历过的目录从队列中删除
queueDirectory.pop();
}//end while (queueDirectory.size() > 0)
if(INVALID_HANDLE_VALUE != hHandle)
{
::FindClose(hHandle);
hHandle = INVALID_HANDLE_VALUE;
}//end if(INVALID_HANDLE_VALUE != hHandle)
}//end if(::GetFileAttributes(szPath) & FILE_ATTRIBUTE_DIRECTORY) // 目录
else
{
HandleFindFiles(szPath);
}//end else
}
对于这种方式,采用的是队列来保存遍历过程中的数据,在这个Demo中,队列queueDirectory是定义在函数内的局部变量,使用的也是栈空间,如果把它定义为全局变量的话,使用的就不是栈空间了。由于在遍历的过程中没有递归的操作,而且在遍历过程中对队列的元素还有动态的删除过程,所以整个过程中,函数调用深度和栈空间的使用都可以得到一定程度的控制。因此在遍历的时候,在用这种方式会更好一些。
测试主程序:
#include <windows.h>
#include <iostream>
#include <queue>
using namespace std;
#define BUFFER_SIZE 0x1000
int g_iFileCount = 0;
/**********************************
函数名称:HandleFindFiles
功 能:依据传进来的文件路径进行处理
参 数:lpszFileName: 传进来的文件路径
返 回 值:VOID
备 注:无
***********************************/
VOID HandleFindFiles(TCHAR *lpszFileName)
{
printf("\r\n");
printf("FileCount == %-20d\r\n", ++g_iFileCount);
//这里只是显示文件名
#ifndef _UNICODE
printf("FileName:%s\r\n", lpszFileName);
#else
//处理Unicode模式下显示中文的问题
setlocale(LC_ALL, "chs"); //设置中文显示
wprintf(TEXT("FileName:%s\r\n"), lpszFileName);
setlocale(LC_ALL, "C"); //还原原来的显示设置
#endif
}
int _tmain(int argc, _TCHAR* argv[])
{
TCHAR szDriverString[BUFFER_SIZE] = {TEXT('\0')};
DWORD dwRet = 0;
dwRet = ::GetLogicalDriveStrings(sizeof(szDriverString)/sizeof(szDriverString[0]) - 1, szDriverString);
if(0 == dwRet || dwRet > (sizeof(szDriverString)/sizeof(szDriverString[0]) - 1))
{
//获取 DriveString 失败,直接返回
return 0;
}//end if(0 == dwRet || dwRet > (sizeof(szDriverString)/sizeof(szDriverString[0]) - 1))
UINT iDriveType = 0;
TCHAR *lpDrive = szDriverString;
while(*lpDrive)
{
iDriveType = ::GetDriveType(lpDrive);
if(DRIVE_FIXED == iDriveType || DRIVE_REMOVABLE == iDriveType)
{
//TravelPathByDFS(lpDrive, FALSE); //深度优先遍历
TravelPathByBFS(lpDrive, FALSE); //广度优先遍历
}//end if(DRIVE_FIXED == iDriveType || DRIVE_REMOVABLE == iDriveType)
lpDrive += lstrlen(lpDrive) + 1;
}//end while(*lpDrive)
return 0;
}

本文介绍如何使用深度优先(DFS)和广度优先(BFS)遍历Windows目录。深度优先采用递归,需要栈,适合递归层次较少的情况;广度优先利用队列,非递归,栈空间控制更优,适用于目录遍历。
和广度优先(BFS)两种方法遍历Windows指定路径&spm=1001.2101.3001.5002&articleId=77931073&d=1&t=3&u=fdaac9e5f05f4008a9d36bf8693b7923)
2950

被折叠的 条评论
为什么被折叠?



