基于ACE实现C++目录遍历

来源:互联网 发布:python 运维开发实例 编辑:程序博客网 时间:2024/05/21 22:55

目录遍历,在软件编程中属于常见的需求;如:病毒扫描、源代码编辑、文件比较。在windows环境,使用FindFirstFileExFindNextFile(详细情况可以参考MSDN)等文件访问函数,能够实现目录遍历;在posix环境,可以使用open_dir。跨平台不是这里讨论的重点。

ACE提供的opendir_emulationreaddir_emulation函数族与WindowsFindFirstFileExFindNextFile功能是一致的;其本意也是提供一种类似接口的跨平台封装。但是,直到版本4.361都还是没有加入其它平台的处理;现在只能认为ACE把一些内存管理包装到自己的函数族中了。

以下的讨论也基于如上的函数实现。

 

目录的组织可以分成3种情况:

1)      下面也是目录;

2)      下面是文件;

3)      .’和‘..

其中‘.’和‘..’比较特殊,需要过滤。基于上述分析,函数递归调用的算法比较简单。为代码如下:

打开目录;

如果是文件,调用文件处理函数;

如果是目录:

如果目录名是‘.’或者‘..’,继续;

对目录下所有内容:

调用本函数;

读取目录;

清除处理

本文讨论的是:如何基于OO的实现。明显的,如上的递归算法可以封装到一个类中,这是不变的地方;变化的是对文件的处理,比如:打印,扫描等。检查算法,文件处理,可以用虚函数,实现hook处理,可以通过重载此函数实现功能拓展。另外,容易考虑到的内容是,文件过滤,如对*.exe或者*.cpp进行某种操作。

有了如上的分析,容易得到如下的类定义:

class DirVisitor

{

public:

        void get_one_dir(const string& parent, const string& dir_name)

        {      

                struct ACE_DIR*  base_dir = ACE_OS::opendir_emulation(parent.c_str());

                if(!base_dir)

                {

        #if 0

                        cout << parent.c_str() << " is a file" << endl;

        #endif

                        if(!bypass(parent.c_str()))

                        {

                                handle_one_file(parent, dir_name);

                        }

                }

                else

                {

                        //remove . and .. directory

                        struct ACE_DIRENT * dir = ACE_OS::readdir_emulation(base_dir);

                        //posix可以换成dir == . || dir == ..这样可以处理隐含文件。

                        while(dir && dir->d_name[0] == '.')

                        {      

                                //posix,隐含文件被忽略。             

                                dir = ACE_OS::readdir_emulation(base_dir);

                        }

                        //recursive call

                        while(dir)     

                        {

                                get_one_dir(parent + "//" + dir->d_name, string(dir->d_name));         

                                dir = ACE_OS::readdir_emulation(base_dir);

                        }

                        ACE_OS::closedir_emulation(base_dir);

                }      

        }

protected:

        virtual int handle_one_file(const string& path_name, const string& dir_name)

        {

                cout << path_name.c_str() << endl;

                return 0;

        }

        virtual bool comparer(const string& name, const string& key)

        {

                if(string::npos != name.find(key))

                {

                        return true;

                }

                return false;

        }

        virtual bool bypass(const string& name)

        {

                //always not by pass

                return false;

        }

};

类的对外借口只有一个open_one_dir,其他的都被封装起来;可以拓展的是3个保护函数:

1)      handle_one_file:提供一种默认处理,打印到标准输出;

2)      comparer:文件名比较方法,默认是字符串包含与否的比较;可以拓展为模糊比较;

3)      bypass:忽略文件的条件;这里默认不忽略。

这样客户端代码就简单了:

int main(int argc, char** argv)

{

        //从命令行参数中传入起始目录

        if(argc == 1)

        {

                cout << "using:" << argv[0] << " [source directory]." << endl;

                exit(0);

               

        }

        DirVisitor visitor;

        visitor.get_one_dir(string(argv[1]), string(""));

}

这样的代码能够显示一个目下的所有目录和文件。如ace/doc目录的扫描结果:

E:/tools/ACE_wrappers/docs/ACE-bug-process.html
E:/tools/ACE_wrappers/docs/ACE-categories.html
E:/tools/ACE_wrappers/docs/ACE-configuration.txt
E:/tools/ACE_wrappers/docs/ACE-development-process.html
E:/tools/ACE_wrappers/docs/ACE-FMM.html
E:/tools/ACE_wrappers/docs/ACE-guidelines.html
E:/tools/ACE_wrappers/docs/ACE-lessons.html
E:/tools/ACE_wrappers/docs/ACE-porting.html
E:/tools/ACE_wrappers/docs/ACE-SSL.html
E:/tools/ACE_wrappers/docs/ACE-subsets.html
E:/tools/ACE_wrappers/docs/ace_guidelines.vsmacros
E:/tools/ACE_wrappers/docs/CE-status.txt
E:/tools/ACE_wrappers/docs/CVS.html
E:/tools/ACE_wrappers/docs/exceptions.html
E:/tools/ACE_wrappers/docs/index.html
E:/tools/ACE_wrappers/docs/msvc_notes.txt
E:/tools/ACE_wrappers/docs/README.tutorials
E:/tools/ACE_wrappers/docs/run_test.txt
E:/tools/ACE_wrappers/docs/usage-bugzilla.html
E:/tools/ACE_wrappers/docs/wchar.txt
Press any key to continue

要进行过滤,只要重载bypass,这里提供一种对c++文件显示的范例:

        bool CPlusVisitor::bypass(const string& name)

        {      

                if(comparer(name, string(".h")))

                {

                        return false;

                }

                if(comparer(name, string(".c")))

                {

                        return false;

                }

                if(comparer(name, string(".C")))

                {

                        return false;

                }

                if(comparer(name, string(".cpp")))

                {

                        return false;

                }

                if(comparer(name, string(".i")))

                {

                        return false;

                }

                if(comparer(name, string(".inl")))

                {

                        return false;

                }

 

                return true;

        }

关于handle_one_file方法:参数1path_name提供了带路径的文件名;参数2key提供不带路径的文件名,方便处理。

小结:这里提供的基于ACE的目录遍历,由于其OO的封装,方便实用;使用ACE的目的就是利用其跨平台的包装。为了目录遍历的跨平台性,可以自己封装posixopendir_emulationreaddir_emulation函数族,也可以等待ACE提供^---^