zoukankan      html  css  js  c++  java
  • C/C++ 遍历目录文件,默认目录下

    每次遇到这样的问题总会折腾很久,到网上搜,或者查资料,弄了很多次,但就是没记住,这次写程序又遇到了,干脆就把它都弄清楚了,然后顺便在这里记录一下,以后再遇到就不用到处去找了。

            用 C/C++ 遍历目录文件主要有两种方式,分别对应在 Windows VS 环境下和 LinuxUnix 环境下的方法,它们各自所使用的函数如下:

    1. (Windows VS)_findfirst, _findnext, _findclose
    2. (LinuxUnix)opendir, readdir, closedir

    下面就来详细地说说这两种方式

    第一(_findfirst, _findnext, _findclose)

      基本流程:_findfirst-->_findnext-->_findclose

     

    (1)First Step

             _findfirst 的函数原型:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. long _findfirst( char *filespec, struct _finddata_t *fileinfo );  

            它返回一个文件句柄,可以作为其他函数的参数,并将文件名匹配由 filespec 指定的模式的第一个文件的信息保存在 fileinfo 里。例如我要找某个目录下的 txt 文件,那么 fileinfo 就保存了这个目录下第一个 txt 文件的信息,这种情况下我们可以这样来调用这个函数:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. long pFile = long _findfirst( "*.txt", fileinfo );  

    而用来保存文件信息的 fileinfo 是一个数据类型为 _finddata_t 的结构体,在头文件IO.H定义:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. struct _finddata_t {  
    2.         unsigned    attrib;              /*文件属性*/  
    3.         time_t      time_create;         /*文件创建时间, -1 for FAT file systems */  
    4.         time_t      time_access;         /*文件最后一次访问的时间 -1 for FAT file systems */  
    5.         time_t      time_write;          /*文件最后一次写的时间*/   
    6.         _fsize_t    size;                /*文件大小*/  
    7.         char        name[_MAX_FNAME];    /*匹配的文件名,不包含目录,_MAX_FNAME在STDLIB.H中定义为256字节*/  
    8. };  

    其中文件属性可以是以下的这些值(MSDN上的说明):

    _A_ARCH(存档)

    Archive. Set whenever the file is changed, and cleared by the BACKUP command. Value: 0x20

    _A_HIDDEN(隐藏文件)

    Hidden file. Not normally seen with the DIR command, unless the /AH option is used. Returns information about normal files as well as files with this attribute. Value: 0x02

    _A_NORMAL(普通文件,没有读写限制)

    Normal. File can be read or written to without restriction. Value: 0x00

    _A_RDONLY(只读文件)

    Read-only. File cannot be opened for writing, and a file with the same name cannot be created. Value: 0x01

    _A_SUBDIR(子目录)

    Subdirectory. Value: 0x10

    _A_SYSTEM(系统文件)

    System file. Not normally seen with the DIR command, unless the /A or /A:S option is used. Value: 0x04

     

    (2)Second Step

            我们已经读取了第一个文件的信息了,那怎么读取下一个呢,这时候就轮到 _findnext 出马了,同样我们先看看其函数原型:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. int _findnext( long handle, struct _finddata_t *fileinfo );  

            这个函数调用如果成功就返回0,否则返回-1。它将下一个文件名匹配 filespec 的文件的信息保存在 fileinfo 里面,handle 是调用 _findfirst 时返回的句柄。比如说我们这里的例子,要得到下一个txt文件的信息,我们可以这样调用函数

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. _findnext(pFile, fileinfo);  

    所以我们不断调用 _findnext 直到它返回-1就可以遍历所有的txt文件了。

     

    (3)Step Three

            函数 _findclose 就是做一些收尾工作,关闭文件句柄:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. int _findclose( long handle );  

            同样地,handle 也是调用 _findfirst 时返回的句柄。

     

    (4)Final

            综上所述,遍历某个目录下指定的文件(所有文件则用*表示)可以这样写:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. #include <io.h>  
    2.   
    3. int main()  
    4. {  
    5.     struct _finddata_t fileinfo;  
    6.     long hFile;  
    7.     if((hFile = _findfirst("*.txt",&fileinfo)) == -1)  
    8.         return -1;  
    9.     else {  
    10.         do {  
    11.             /*Process File*/  
    12.         }while (_findnext(hFile,&file)==0);  
    13.     }  
    14.     _findclose(hFile);  
    15.     return 0;  
    16. }  

    第二(opendir, readdir, closedir)

      基本流程:opendir-->readdir-->closedir

     

    (1)Step One

           使用这些函数我们需要包含头文件 <sys/types.h> 和 <dirent.h>。

            我们要读取目录下的文件信息,首先要打开目录,也就是要调用函数 opendir:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. DIR *opendir(const char *pathname);  

            这个函数以目录的路径为参数,如果打开成功,就返回一个DIR类型的指针,相当于上面所说的句柄,用于后续函数调用;否则返回 NULL。所以如果要打开当前目录可以这样调用函数:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. DIR *pDir = opendir(".");  

            这样我们就打开了目录,返回值 pDir 用于接下来函数调用的参数。

     

    (2)Step Two

            接下来就是读取文件的信息了 ,在 <dirent.h> 中定义了一个结构体 dirent ,用来保存文件信息,定义如下:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. struct dirent {  
    2.     ino_t          d_ino;       /* i-inode number */  
    3.     off_t          d_off;       /* offset to the next dirent */  
    4.     unsigned short d_reclen;    /* length of this record */  
    5.     unsigned char  d_type;      /* type of file */  
    6.     char           d_name[256]; /* null-terminated filename */  
    7. };  

            这里面其实经常要用到的就是 d_type 和 d_name 这两个字段,分别表示文件类型和文件名。

            d_type 表示文件类型,其取值定义如下:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. enum  
    2. {  
    3.     DT_UNKNOWN = 0,  
    4. # define DT_UNKNOWN DT_UNKNOWN  
    5.     DT_FIFO = 1,  
    6. # define DT_FIFO DT_FIFO  
    7.     DT_CHR = 2,  
    8. # define DT_CHR DT_CHR  
    9.     DT_DIR = 4,  
    10. # define DT_DIR DT_DIR  
    11.     DT_BLK = 6,  
    12. # define DT_BLK DT_BLK  
    13.     DT_REG = 8,  
    14. # define DT_REG DT_REG  
    15.     DT_LNK = 10,  
    16. # define DT_LNK DT_LNK  
    17.     DT_SOCK = 12,  
    18. # define DT_SOCK DT_SOCK  
    19.     DT_WHT = 14  
    20. # define DT_WHT DT_WHT  
    21. };  

            然后使用 readdir 这个函数来读取文件信息:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. struct dirent *readdir(DIR *dp);  

            dp 为调用 opendir 时的返回值。当读取成功时,函数返回一个保存文件信息的 dirent 结构体指针,当到达目录末尾或出错时返回 NULL。每次调用该函数时读取下一个文件的信息,所以不断调用函数直到它返回 NULL,就可以遍历该目录下所有的文件:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. struct dirent * ptr = readdir(pDir);  

     

    (3)Step Three

            最后这一步也是收尾工作,关闭目录:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. int closedir(DIR *dp);  

            同样的,dp 为调用 opendir 时的返回值,成功时返回0,失败时返回-1。

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. closedir(pDir);  

     

    (4)Final

            因此,总的遍历目录文件的程序可以这样写:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. #include <stdio.h>  
    2. #include <sys/types.h>  
    3. #include <dirent.h>  
    4.   
    5. int main()  
    6. {  
    7.     DIR* pDir;  
    8.     struct dirent* ptr;  
    9.     if( !(dir = opendir(".")) )  
    10.         return -1;  
    11.     while( (ptr = readdir(pDir)) != 0 )  
    12.     {  
    13.         /*Processing File*/  
    14.     }  
    15.     closedir(pDir);  
    16.     return 0;  
    17. }  

    总结

            一般来说,第一种方法只能在 Windows VS 环境下用,而第二种方法只能在 Linux/Unix 环境下使用。因为在用 VS 写程序的过程中想用第二种方法看看能不能用,结果是找不到头文件 <dirent.h>,而且从结构体 dirent 的定义中我们就可以看出,它只能在 Linux 下使用,因为 dirent 的定义中有个字段是表示文件的 i-node number,这个恰恰是在Linux 的文件管理中才有的。当在 Linux 想用第二种方法时则找不到头文件 <io.h>。但网络上可能有一些方法可以两种方法在两个平台上都可以使用,这个我倒没有去搜索相关的资料,如果大家有什么好的办法,可以分享一下。上面的两个程序我都已经测试过了,用的是 VS2012 和 Ubuntu。

            另外,由上面的解说可以看到,在 Windows 下可以很容易地读取某一类型的文件的信息(比如 txt 文件),但在 Linux 下则需要多花些功夫(比如要查看 txt 文件,你得读取文件信息然后再判断文件扩展名是不是 .txt)。

  • 相关阅读:
    关于mybatis中的#{},和${} 的区别
    免费的手机号码归属地查询API接口文档
    小程序即将上线,现在就可以开发啦
    (二)cordova+framework7入门——笑笑APP
    (一)半小时开发一个APP
    图灵机器人(问答机器人)API调用示例
    免费股票数据API接口
    CTO和技术副总裁应该如何分工?谁才是技术领导者?
    基于JAVA的全国天气预报接口调用示例
    PhpSms 稳定可靠的php短信发送库
  • 原文地址:https://www.cnblogs.com/1996313xjf/p/5911126.html
Copyright © 2011-2022 走看看