Linux和Windows的遍歷目錄下所有文件的方法對比


首先兩者讀取所有文件的方法都是采用迭代的方式,首先用函數A的返回值判斷目錄下是否有文件,然后返回值合法則在循環中用函數B直到函數B的返回值不合法為止。最后用函數C釋放資源。

1、打開目錄

#include <sys/types.h>
#include <dirent.h>

DIR *opendir(const char *name);

先看Linux的,返回的是DIR*,因此出錯時返回NULL(0)。而這里不用關心DIR結構具體定義,只需要知道是對它進行操作(注意:DIR不是保存文件信息的結構)

而Windows的方法很多,包括MFC的CFileFind類、WINAPI的WIN32_FIND_DATA和C運行庫的_finddata_t,這里選取最后一種

intptr_t _findfirst(  
   const char *filespec,  
   struct _finddata_t *fileinfo   
);  

返回類型是intptr_t,這是用來表示兩個指針(地址)之間距離的類型(比如對指針類型p1,p2,用intptr_t來接收p1-p2的返回值),比如指針在32位系統上是4個字節,而在64位系統上是8個字節,通過#ifdef宏來實現跨平台的類型。

這里返回值其實是一個標識當前目錄下所有文件的HANDLE而不是實際指針類型,所以出錯時返回-1而不是0(NULL)。

再看輸入參數,第1個參數是filespec(而不是filename),很容易用錯,因為它不是像Linux的opendir一樣簡單地接收目錄名,而是接收一個特定格式。比如C:\*.*就代表搜索C盤下所有類型文件,而C:\*.txt則代表搜索C盤下所有txt文件。第2個參數是struct _finddata_t是實際存儲文件信息的結構。

2、遍歷文件

每個文件都有一個具體的結構來描述它的屬性,這里只以文件名作為示例,其他屬性不具體探,具體定義可以查找文檔。

#include <dirent.h>

struct dirent *readdir(DIR *dirp);

Linux下的方法依然很簡單,通過第一步得到的DIR*作為輸入參數,方法成功則返回指向當前文件的dirent*,struct dirent即保存文件信息的結構

           struct dirent {
               ino_t          d_ino;       /* Inode number */
               off_t          d_off;       /* Not an offset; see below */
               unsigned short d_reclen;    /* Length of this record */
               unsigned char  d_type;      /* Type of file; not supported
                                              by all filesystem types */
               char           d_name[256]; /* Null-terminated filename */
           };

如果讀取失敗則返回空指針NULL(0)

再看Windows下的方法

int _findnext(  
   intptr_t handle,  
   struct _finddata_t *fileinfo   
);  

這里返回值是int,依舊是出錯時返回-1。(C沒有異常處理機制,而是采用朴素的錯誤碼機制,於是誕生了讓初學者感到很困惑的問題:返回0到底是代表正確還是不正確呢?對錯誤碼而言,0往往代表正確,而對指針而言0則代表失敗,也就是空指針NULL)

第1個輸入參數也是第1個函數的返回值,第2個輸入參數也是描述文件的結構體的指針。struct _finddata_t即保存文件信息的結構,它也是個跨(Windows)平台的定義,以32位系統為例

struct _finddata32_t
{
    unsigned    attrib;
    __time32_t  time_create;    // -1 for FAT file systems
    __time32_t  time_access;    // -1 for FAT file systems
    __time32_t  time_write;
    _fsize_t    size;
    char        name[260];
};

3、關閉目錄

#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);

Linux下的,輸入參數是第1個函數的返回值,而這里返回值不再是指針,而是錯誤碼,因此返回值為0時代表關閉directory stream成功,為-1代表失敗

int _findclose(   
   intptr_t handle   
);  

Windows下的也一樣,輸入參數是第1個函數的返回值,返回0代表關閉handle成功,為-1代表失敗。

 

最后分別給出Linux和Windows上遍歷目錄下所有文件的示例代碼(為了簡化忽略錯誤處理)

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

int main(int argc, char** argv)
{
   struct dirent *direntp;
   DIR *dirp = opendir("/");

   if (dirp != NULL) {
       while ((direntp = readdir(dirp)) != NULL)
           printf("%s\n", direntp->d_name);
   }

   closedir(dirp);
   return 0;
}
// Windows(C++)
#include <stdio.h>
#include <stdlib.h>
#include <io.h>  // windows的CRT庫
#include <string>

int main()
{
	_finddata_t fd;
	intptr_t handle;
	std::string dir_name = "C:\\";

	if ((handle = _findfirst((dir_name + "*.*").c_str(), &fd)) != -1) {
		while (_findnext(handle, &fd) != -1)
			printf("%s\n", fd.name);
	}

	_findclose(handle);
	return 0;
}

由於Windows下還要對dir_name附上一段字符串所以直接用std::string了,用char數組然后strcpy太麻煩。

對比可以發現,Windows是把文件結構的指針作為輸入參數,而Linux則是作為返回參數,Linux下的這種做法更為自然,而且即使用的是C風格,代碼也非常簡單易懂。

-----------------------------------------------------------下面是之前的錯誤看法--------------------------------------------------------------

但是要注意,Linux這種做法實際上是動態申請了空間,需要手動free(direntp)來釋放內存,雖然APUE上面的示例代碼並沒有這一步。

-----------------------------------------------------------上面是之前的錯誤看法--------------------------------------------------------------

readdir是不可重入的函數,按照man的說明,readdir()的返回值會被接下來的調用給重寫

The data returned by readdir() may be overwritten by subsequent calls
to readdir() for the same directory stream.

也就是說readdir的實現其實是類似這種

struct dirent *readdir(DIR *dp)
{
     static struct dirent dir;
     // ...
     return &dir;      
}

而不是使用動態申請內存然后再返回。修改上面列出的Linux遍歷目錄的示例代碼,在while ((dirent p = readdir(dirp)) != NULL)的循環體內printf后面加一句delete dirp或者free(dirp)都會報錯,而且是特別嚴重的Aborted (core dumped)。

 

看來當初自作聰明以為APUE上沒顧及到內存的釋放的我還是太嫩了,繼續啃APUE~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM