遍歷目錄的主要思想
由於目錄就是一顆樹,所以遍歷目錄就轉換為遍歷一棵樹。談到樹的遍歷就再熟悉不過了,有樹的前序、層次和后序遍歷,我使用的是前序遍歷,后序遍歷和前序遍歷本質上一樣,而層次遍歷要比前兩個麻煩些,我兩個都實現了,現在貼出來分享下。
前序遍歷
前序遍歷和樹的遍歷一樣,我先顯示當前目錄的信息,然后遍歷目錄中的目錄項,如果目錄項是一個目錄則先遞歸這個子目錄,否則如果是目錄項是非目錄的話就返回。
1 static void DoTraverDir(MyFunc myFunc) 2 { 3 struct stat statBuf; 4 DIR *pDir; 5 struct dirent *pDirent; 6 int pathLen, direntLen; 7 8 // printf("path: %s\n", fullPath); 9 //獲取當前目錄信息,用lstat防止嵌套調用 10 if (lstat(fullPath, &statBuf) == 0) 11 myFunc(fullPath, &statBuf, TD_F); 12 else 13 { 14 myFunc(fullPath, &statBuf, TD_NS); //無法獲取信息 15 return; 16 } 17 if (!S_ISDIR(statBuf.st_mode)) //當前路徑為文件 18 return; 19 20 //處理目錄 21 pathLen = strlen(fullPath); 22 if (pathLen >= MAX_PATH - 2) //目錄長度限制,不再處理更深層的目錄 23 return; 24 if ((pDir = opendir(fullPath)) == NULL) 25 { 26 myFunc(fullPath, &statBuf, TD_DNR); //無法讀取目錄 27 return; 28 } 29 if (fullPath[pathLen - 1] != '/') 30 fullPath[pathLen++] = '/'; 31 //遍歷目錄中的所有目錄項 32 while ((pDirent = readdir(pDir)) != NULL) 33 { 34 //忽略.和..目錄項 35 if (strcmp(pDirent->d_name, ".") == 0 || 36 strcmp(pDirent->d_name, "..") == 0) 37 continue; 38 direntLen = strlen(pDirent->d_name); 39 if (pathLen + direntLen > MAX_PATH) //路徑超過了最大長度 40 return; 41 strcpy(fullPath + pathLen, pDirent->d_name); 42 fullPath[pathLen + direntLen] = 0; 43 DoTraverDir(myFunc); //遞歸處理下一層 44 } 45 fullPath[pathLen - 1] = 0; 46 if (closedir(pDir) == -1) 47 printf("close dir error : %s\n", fullPath); 48 }
代碼中的fullPath是一個全局變量,用來存放當前遍歷文件的路徑,路徑的最大長度為4096,超過了4096函數自己返回,不進行任何的處理,定義如下:
1 #define MAX_PATH 4096 2 static char fullPath[MAX_PATH + 1];
還有一個地方值得注意,那就是我上面紅色標識的代碼,遍歷完目錄后必須將當前目錄關閉掉,否則程序占有的打開目錄資源會超過系統的限制,當遍歷到了一定的數量后,后面的遍歷都會失敗,我就是開始沒有關閉目錄,所以后面出現莫名其妙的錯誤。
層次遍歷
層次遍歷要比前序遍歷復雜點,因為是要先處理好了當前目錄中的所有目錄項后再處理下一層的目錄。所有在遍歷當前目錄的目錄項時必須保存下層目錄的路徑信息,以方便處理下層目錄。不過和前面代碼的實現也差不多,就是多了一個保存路徑的容器罷了,代碼如下:
1 static void DoTraverDir(MyFunc myFunc) 2 { 3 struct stat statBuf; 4 DIR *pDir; 5 struct dirent *pDirent; 6 int pathLen, direntLen; 7 std::vector<std::string> vpDirent; 8 std::vector<std::string>::iterator vpDirentIterator; 9 std::string str; 10 11 // printf("path: %s\n", fullPath); 12 //獲取當前目錄信息,用lstat防止嵌套調用 13 if (lstat(fullPath, &statBuf) == 0) 14 myFunc(fullPath, &statBuf, TD_F); 15 else 16 { 17 myFunc(fullPath, &statBuf, TD_NS); //無法獲取信息 18 return; 19 } 20 //處理目錄 21 pathLen = strlen(fullPath); 22 if (pathLen >= MAX_PATH - 2) //目錄長度限制,不再處理更深層的目錄 23 return; 24 if ((pDir = opendir(fullPath)) == NULL) 25 { 26 myFunc(fullPath, &statBuf, TD_DNR); //無法讀取目錄 27 return; 28 } 29 if (!S_ISDIR(statBuf.st_mode)) //當前路徑為文件 30 return; 31 if (fullPath[pathLen - 1] != '/') 32 fullPath[pathLen++] = '/'; 33 //遍歷目錄中的所有目錄項 34 35 while ((pDirent = readdir(pDir)) != NULL) 36 { 37 //忽略.和..目錄項 38 if (strcmp(pDirent->d_name, ".") == 0 || 39 strcmp(pDirent->d_name, "..") == 0) 40 continue; 41 direntLen = strlen(pDirent->d_name); 42 if (pathLen + direntLen > MAX_PATH) //路徑超過了最大長度 43 return; 44 strcpy(fullPath + pathLen, pDirent->d_name); 45 fullPath[pathLen + direntLen] = 0; 46 if (lstat(fullPath, &statBuf) != 0) 47 { 48 myFunc(fullPath, &statBuf, TD_NS); //無法獲取信息 49 continue; 50 } 51 if (S_ISDIR(statBuf.st_mode)) //當前路徑為目錄 52 vpDirent.push_back(pDirent->d_name); 53 else 54 myFunc(fullPath, &statBuf, TD_F); 55 } 56 if (closedir(pDir) == -1) 57 { 58 fullPath[pathLen - 1] = 0; 59 printf("close dir error : %s\n", fullPath); 60 } 61 62 for (vpDirentIterator = vpDirent.begin(); vpDirentIterator != vpDirent.end(); ++vpDirentIterator) 63 { 64 str = *vpDirentIterator; 65 direntLen = str.length(); 66 strcpy(fullPath + pathLen, str.c_str()); 67 fullPath[pathLen + direntLen] = 0; 68 DoTraverDir(myFunc); 69 } 70 }
回調函數
上面程序中的回調函數MyFunc是用來顯示每個文件的信息和對所遍歷信息的統計,下面是該函數的具體實現:
1 void ShowInfo(const char *pathName, const struct stat *statBuf, int type) 2 { 3 switch (type) 4 { 5 case TD_F: 6 if (S_ISREG(statBuf->st_mode)) //普通文件 7 nReg++; 8 else if (S_ISDIR(statBuf->st_mode)) //目錄 9 nDir++; 10 else if (S_ISCHR(statBuf->st_mode)) //字符文件 11 nChr++; 12 else if (S_ISBLK(statBuf->st_mode)) //塊文件 13 nBlk++; 14 else if (S_ISFIFO(statBuf->st_mode)) //管道文件 15 nFifo++; 16 else if (S_ISLNK(statBuf->st_mode)) //鏈接文件 17 nLink++; 18 else if (S_ISSOCK(statBuf->st_mode)) //套接字文件 19 nSock++; 20 else //未知文件類型 21 { 22 nKno++; 23 printf("unknow file type : %s\n", pathName); 24 } 25 break; 26 case TD_NS: 27 nNs++; 28 printf("can't state file : %s\n", pathName); 29 break; 30 case TD_DNR: 31 nDnr++; 32 printf("can't open dir : %s\n", pathName); 33 break; 34 default: 35 printf("unkonw type\n"); 36 } 37 }