在Linux下和Windows下遍历目录的方法及如何达成一致性操作
最近因为测试目的需要遍历一个目录下面的所有文件进行操作,主要是读每个文件的内容,只要知道文件名就OK了。在Java中直接用File类就可以搞定,因为Java中使用了组合模式,使得客户端对单个文件和文件夹的使用具有一致性,非常方便。但在C中就不一样了,而且在不同的平台下使用方法也不同。在Linux下实现该功能就非常方便,因为自带有API库,几个函数用起来得心应手(虽然有些小问题,后面说),在Windows下实现就不是那么方便,虽然也有自己的API,但用法有些晦涩难懂,因为没有封装起来,需要自己一步一步进行操作,因为用的是Windows API库函数所以如果对Windows编程不熟悉的话,照搬网上的代码错了也不易调试。为此,我把这些操作都封装成类似Linux下的库函数,一方面简化透明了操作,另一方面(也许更重要)就是移植性,这样将包含该功能的程序从Windows上移植到Linux下就无需改动代码了(删掉实现封装的文件,因为Linux下自带了),当然从Linux下移植到Windows下同样方便(增加实现封装的文件即可),这就是所谓的OCP原则吧(开放封闭原则,具体见:程序员该有的艺术气质—SOLID原则)。好了,首先看下Linux下是如何实现这个功能的。 Linux下实现目录操作的API函数都在头文件dirent.h中,截取部分该文件内容如下: /** structure describing an open directory. */ typedef struct _dirdesc { int dd_fd; /** file descriptor associated with directory */ long dd_loc; /** offset in current buffer */ long dd_size; /** amount of data returned by getdirentries */ char *dd_buf; /** data buffer */ int dd_len; /** size of data buffer */ long dd_seek; /** magic cookie returned by getdirentries */ long dd_rewind; /** magic cookie for rewinding */ int dd_flags; /** flags for readdir */ struct pthread_mutex *dd_lock; /** lock */ struct _telldir *dd_td; /** telldir position recording */ } DIR; typedef void * DIR; DIR *opendir(const char *); DIR *fdopendir(int); struct dirent *readdir(DIR *); void seekdir(DIR *, long); long telldir(DIR *); void rewinddir(DIR *); int closedir(DIR *); struct dirent { long d_ino; /* inode number*/ off_t d_off; /* offset to this dirent*/ unsigned short d_reclen; /* length of this d_name*/ unsigned char d_type; /* the type of d_name*/ char d_name[1]; /* file name (null-terminated)*/ }; 关键部分就是DIR这个结构体的定义,包括文件描述符、缓冲区偏移、大小、缓冲区内容等,下面定义的就是具体的目录操作函数了,有打开目录、读目录、重置读取位置、关闭目录等,这里我所需要的就是打开、读和关闭这三个最基本的目录操作,下面是使用例子: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <dirent.h> #define MAX_LEN 65535 int main(void) { DIR *dir; struct dirent *ptr; char *flow[MAX_LEN]; int num = 0, i = 0; if ((dir=opendir("./data")) == NULL) { perror("Open dir error..."); exit(1); } // readdir() return next enter point of directory dir while ((ptr=readdir(dir)) != NULL) { flow[num++] = ptr->d_name; // printf("%sn", flow[num - 1]); } for(i = 0; i < num; i++) { printf("%sn", flow[i]); } closedir(dir); } 运行结果如下: 一看这结果就不对,输出的都是同一个文件名(最后一个文件的文件名), 哪里出了问题呢?将代码中// printf("%sn", flow[num - 1]);这行注释去掉再运行,发现注释处输出的是正确的,两者都是输出的flow数组元素怎么结果不一样呢?经过调试发现是flow[num++] = ptr->d_name;这句代码的问题,因为这是引用拷贝(地址拷贝),所有的flow元素全部指向同一个对象ptr->d_name,虽然ptr->d_name对象每次的内容不同(也就是前面正确输出的原因),但所有内容都共享一个地址,用一个简单的图说明就是: 当然这个问题也比较好解决,也是比较常见的问题,用字符串拷贝或内存拷贝就行了,给flow每个元素重新申请一块内存。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <dirent.h> #define MAX_LEN 65535 int main(void) { DIR *dir; struct dirent *ptr; char *flow[MAX_LEN]; int num = 0, i = 0; if ((dir=opendir("./data")) == NULL) { perror("Open dir error..."); exit(1); } // readdir() return next enter point of directory dir while ((ptr=readdir(dir)) != NULL) { flow[num] = (char*)malloc(sizeof(char)); strcpy(flow[num], ptr->d_name); num++; } for(i = 0; i < num; i++) { printf("%sn", flow[i]); } closedir(dir); } 最终结果就正确了。 二、Windows下遍历目录的方法 在Windows下就比较麻烦了,所要用到的函数都在windows.h中,Windows编程本来就比较繁琐,下面就不一一介绍所用到的函数了,直接给出封装的过程。 1. 首先模拟Linux下自带的头文件dirent.h (编辑:应用网_丽江站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |