• 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
    ", 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
    ", 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~

  • 相关阅读:
    默比乌斯函数
    勒让德符号相关
    微积分入门("SX"T版)
    分治法求2n个数的中位数
    SSM框架学习之高并发秒杀业务--笔记1-- 项目的创建和依赖
    10月9日Android学习笔记:活动与服务之间的通信
    高精度加法
    Windows环境下多线程编程原理与应用读书笔记(4)————线程间通信概述
    郁闷
    素数链
  • 原文地址:https://www.cnblogs.com/Harley-Quinn/p/6367425.html
Copyright © 2020-2023  润新知