首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言獲取目錄文件列表:system+dir 臨時文件法與 Windows API 正規軍對比

     編輯:關於C語言

在 Windows 平台獲取目錄下所有文件名,最快原型用 system("dir /b") 重定向到臨時文件再讀取;生產代碼應使用 FindFirstFile/FindNextFile Windows API。本文對比兩種方案的代碼、優缺點、避坑指南。

方案一:system + dir 重定向 (快速原型/腳本)

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main() {
    const char *target_dir = "E:\\Ock\\DirSave";
    const char *tmp_file = "D:\\buf.tmp";
    
    // 1. 執行 dir /b 只輸出文件名,重定向到臨時文件
    char cmd[512];
    snprintf(cmd, sizeof(cmd), "dir /b \"%s\" > \"%s\"", target_dir, tmp_file);
    if (system(cmd) != 0) {
        fprintf(stderr, "dir 命令失敗\n");
        return 1;
    }
    
    // 2. 讀取臨時文件
    FILE *fp = fopen(tmp_file, "r");
    if (!fp) { perror(tmp_file); return 1; }
    
    char line[260];
    char *last = NULL;
    while (fgets(line, sizeof(line), fp)) {
        // 去除換行符
        line[strcspn(line, "\r\n")] = '\0';
        if (line[0]) last = _strdup(line);  // 保存最後一行
    }
    fclose(fp);
    
    // 3. 刪除臨時文件
    char del_cmd[512];
    snprintf(del_cmd, sizeof(del_cmd), "del /q \"%s\"", tmp_file);
    system(del_cmd);
    
    if (last) {
        printf("最後一個文件: %s\n", last);
        free(last);
    } else {
        puts("目錄為空");
    }
    return 0;
}

關鍵點

  • dir /b:裸格式,無表頭/摘要/日期,僅文件名,適合解析。
  • > file:Shell 重定向,覆蓋寫入。
  • del /q:安靜刪除,無確認提示。
  • 缺陷:依賴 cmd.exe、路徑含空格需加引號、臨時文件競爭、性能差、無法獲取屬性/大小/時間。

方案二:Windows API 正規軍 (生產級)

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

void list_files(const TCHAR *dir) {
    WIN32_FIND_DATA ffd;
    TCHAR search_path[MAX_PATH];
    
    // 構建搜索模式:目錄\*
    _stprintf_s(search_path, MAX_PATH, _T("%s\\*"), dir);
    
    HANDLE hFind = FindFirstFile(search_path, &ffd);
    if (hFind == INVALID_HANDLE_VALUE) {
        _tprintf(_T("FindFirstFile 失敗 (%lu)\n"), GetLastError());
        return;
    }
    
    do {
        // 跳過 . 和 ..
        if (_tcscmp(ffd.cFileName, _T(".")) == 0 ||
            _tcscmp(ffd.cFileName, _T("..")) == 0)
            continue;
        
        if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            _tprintf(_T("[DIR]  %s\n"), ffd.cFileName);
        } else {
            // 文件大小:組合高低 32 位
            ULONGLONG size = ((ULONGLONG)ffd.nFileSizeHigh << 32) | ffd.nFileSizeLow;
            _tprintf(_T("%s  %llu bytes\n"), ffd.cFileName, size);
        }
    } while (FindNextFile(hFind, &ffd));
    
    DWORD err = GetLastError();
    FindClose(hFind);
    if (err != ERROR_NO_MORE_FILES)
        _tprintf(_T("FindNextFile 錯誤 (%lu)\n"), err);
}

int main() {
    list_files(_T("E:\\Ock\\DirSave"));
    return 0;
}

API 版優勢

  • 無臨時文件、無 Shell 依賴、無注入風險。
  • 可獲取完整屬性:大小、時間、屬性、備用數據流。
  • 支持 Unicode 路徑 (TCHAR/_tprintf),長路徑前綴 \\\\?\\。
  • 可遞歸遍歷子目錄 (配合 FILE_ATTRIBUTE_DIRECTORY 遞歸調用)。

跨平台考量:dirent.h (POSIX/Linux/macOS)

#include <dirent.h>
#include <stdio.h>

void list_dir_posix(const char *path) {
    DIR *d = opendir(path);
    if (!d) { perror(path); return; }
    struct dirent *ent;
    while ((ent = readdir(d))) {
        if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue;
        printf("%s\n", ent->d_name);
    }
    closedir(d);
}

方案對比總結

維度system+dirWindows APIdirent.h
代碼量極少中等極少
性能低 (進程創建+IO)高 (內核遍歷)高
信息豐富度僅文件名完整屬性文件名+類型(d_type)
Unicode/長路徑麻煩原生支持原生支持
適用場景測試腳本、一次性任務Windows 生產代碼跨平台庫/工具

工程建議

  1. 項目中封裝統一接口:int list_dir(const char*, FileCallback, void*),內部 `#ifdef _WIN32` 分派。
  2. 避免 system():安全審計會標記為命令注入風險,且效率極低。
  3. 長路徑支持:Windows 10 1607+ 開啟「啟用 Win32 長路徑」組策略,或手動加 \\\\?\\ 前綴。
  4. 錯誤處理:API 版必須檢查 GetLastError(),區分「無更多文件」與真實錯誤。

從「借用 Shell 一鍵搞定」到「調用系統 API 精準控制」,是 C 開發者從腳本思維向系統編程思維進化的標誌性一步。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved