核心代碼(Linux/POSIX)
#include
#include
#include
#include
#include
#include
#include
#define MAX_PATH 512
unsigned long dir_count = 0, file_count = 0;
void listdir(const char *path) {
DIR *dp = opendir(path);
if (!dp) {
perror("opendir");
return;
}
struct dirent *entry;
char child_path[MAX_PATH];
char file_path[MAX_PATH];
while ((entry = readdir(dp)) != NULL) {
// 跳過 . 和 ..
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
// 構建完整路徑
snprintf(child_path, MAX_PATH, "%s/%s", path, entry->d_name);
if (entry->d_type == DT_DIR) {
printf("[DIR] %s\n", child_path);
dir_count++;
listdir(child_path); // 遞歸遍歷子目錄
} else if (entry->d_type == DT_REG) {
snprintf(file_path, MAX_PATH, "%s/%s", path, entry->d_name);
printf("[FILE] %s\n", file_path);
file_count++;
}
// 其他類型(符號鏈接、設備文件等)可按需擴展
}
closedir(dp);
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s \n", argv[0]);
return 1;
}
listdir(argv[1]);
printf("\nTotal DIRs: %lu, Total FILEs: %lu\n", dir_count, file_count);
return 0;
}
關鍵 API 解析
| 函數/結構 | 作用 | 備註 |
opendir | 打開目錄流 | 返回 DIR*,失敗返回 NULL |
readdir | 讀取下一個目錄項 | 返回 struct dirent*,結束返回 NULL |
closedir | 關閉目錄流 | 必須調用,否則洩漏文件描述符 |
struct dirent | 目錄項結構 | d_name 文件名,d_type 類型 (DT_DIR/DT_REG/DT_LNK...) |
snprintf | 安全格式化路徑 | 防止緩衝區溢出,替代 sprintf |
d_type 可移植性補充
部分文件系統/內核版本不支持 d_type(返回 DT_UNKNOWN)。通用做法:
struct stat st;
stat(full_path, &st);
if (S_ISDIR(st.st_mode)) { /* 目錄 */ }
else if (S_ISREG(st.st_mode)) { /* 普通文件 */ }
雖多一次系統調用,但完全可移植。
Windows 版本(_findfirst/_findnext)
#ifdef _WIN32
#include
void listdir_win(const char *path) {
char search_path[MAX_PATH];
snprintf(search_path, MAX_PATH, "%s\\*", path);
struct _finddata_t fd;
intptr_t handle = _findfirst(search_path, &fd);
if (handle == -1) return;
do {
if (strcmp(fd.name, ".") == 0 || strcmp(fd.name, "..") == 0) continue;
char full_path[MAX_PATH];
snprintf(full_path, MAX_PATH, "%s\\%s", path, fd.name);
if (fd.attrib & _A_SUBDIR) {
printf("[DIR] %s\n", full_path);
dir_count++;
listdir_win(full_path);
} else {
printf("[FILE] %s\n", full_path);
file_count++;
}
} while (_findnext(handle, &fd) == 0);
_findclose(handle);
}
#endif
增強功能:文件過濾、大小統計、深度限制
typedef struct {
const char *ext_filter; // 如 ".c", NULL 為不限
int max_depth; // -1 無限制
unsigned long total_size;
} WalkConfig;
void listdir_ex(const char *path, int depth, WalkConfig *cfg) {
if (cfg->max_depth >= 0 && depth > cfg->max_depth) return;
// ... 遍歷邏輯 ...
if (entry->d_type == DT_REG) {
struct stat st;
stat(full_path, &st);
if (!cfg->ext_filter || strstr(entry->d_name, cfg->ext_filter))
cfg->total_size += st.st_size;
}
}
最佳實踐總結
- 錯誤處理:
opendir/stat/malloc 必須檢查返回值。
- 資源管理:
closedir、_findclose、free 嚴格配對。
- 緩衝區安全:使用
snprintf、PATH_MAX (limits.h) 替代固定大小。
- 符號鏈接保護:避免循環鏈接導致無限遞歸,記錄已訪問 inode (stat.st_ino) 或限制深度。
- 權限錯誤靜默跳過:
opendir 失敗僅打印警告繼續,不中斷整個遍歷。
- 大目錄性能:考慮
fts (BSD) 或 nftw (POSIX) 替代手寫遞歸,支持並發、排序、過濾。
- 上一篇文章: C語言連接 MySQL 數據庫完整實戰:Linux 環境配置、編譯鏈接、CRUD 操作與資源管理
- 下一篇文章: C 標準庫源碼深度解讀(VC9.0):assert.h 斷言機制、_wassert 實現與 Windows API 交互
Copyright ©
程式碼之家 All Rights Reserved