lovingxiaobing 发表于 2019-2-22 19:31

[WIN32|STL]C++实现python的os模块的walk函数



大家在学习python过程中,应该也遇到使用遍历目录的功能。可能是作业或者其他的,既然是python,肯定是用轮子就对了!!
在python中,遍历一个目录可以使用,os.list配合os.chdir一起使用遍历目录,但是这可是python,还有一个函数就是os.walk。


先放一张图来装X~~~上面有walk函数的用途,以及每个参数的含义。





粗略来说就是:给定一个目录,然后walk函数去遍历它。遍历的结果为目录,也有可能是文件,结果会以(目录字符串,子目录名字符串列表,目录下文件名字符串列表)这样的元组的方式存在一张列表里。
ok!这就是功能与要求,额外的功能就是这些个元素按照怎么样的顺序存在这个结果列表里(源代码中的topdown)。


下面是全部源代码:

/***************************************************
*作者: 小冰
*邮箱: lovingxiaobing@qq.com
*备注: 遍历目录的结果保存到STL容器中
*      函数的功能类似Python的os模块的walk函数
***************************************************/

#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif


#include <tchar.h>
#include <Windows.h>
#include <string>
#include <tuple>
#include <list>
#include <algorithm>
#include <iostream>


#if defined(UNICODE) || defined(_UNICODE)
#define str std::wstring
#define get_input(value) std::wcin>>value
#else
#define str std::string
#define get_input(value) std::cin>>value
#endif

using std::endl;
using std::list;
using std::tuple;
using std::make_tuple;
using std::for_each;

typedef tuple<str,list<str>,list<str>> walk_list_element_type;
typedef list<walk_list_element_type> walk_list;

void walk( str& path, walk_list& rtn_list, bool topdown=true ) {
    DWORD dwFileAttr = GetFileAttributes(path.c_str());
    if ( INVALID_FILE_ATTRIBUTES != dwFileAttr ) {
      /// 只有是目录才遍历
      if ( (FILE_ATTRIBUTE_DIRECTORY & dwFileAttr) == FILE_ATTRIBUTE_DIRECTORY ) {
            WIN32_FIND_DATA wfd;
            /// 开始搜索
            HANDLE handle = FindFirstFile((path + str(_T("\\*"))).c_str(), &wfd);
            if ( INVALID_HANDLE_VALUE != handle ) {

                size_t times = 2;
                str _path_file = path + str(_T("\\"));

                list<str> directorys;
                list<str> files;

                /// 搜索下一个
                while ( FindNextFile(handle, &wfd) ) {

                  str name = str(wfd.cFileName);

                  if ( (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY ) {

                        // 每个目录中只有一个 . 和 .. 目录, 过滤完了2次也就完了
                        // 还有一个问题就是如果目录很多,数量可以达到size_t所能容大的数的大小也没关系
                        // 因为即使那么多目录,也就使用2次lstrcmp,再次重新进入下一轮的--,所以忽略不计
                        // 总比一直调用函数效率高!
                        if ( times-- ) {
                            if ( !lstrcmp(wfd.cFileName, _T(".")) || !lstrcmp(wfd.cFileName, _T("..")) )
                              continue;
                        }
                        
                        if ( topdown )
                            directorys.push_front(name);
                        else
                            directorys.push_back(name);

                        str _dir_entry(_path_file + name);
                        // 进入目录再次遍历
                        walk(_dir_entry, rtn_list);
                  }
                  else {
                        files.push_back(name);
                  }
                }

                if ( topdown )
                  rtn_list.push_front(make_tuple(path, directorys, files));
                else
                  rtn_list.push_back(make_tuple(path, directorys, files));
               

                FindClose(handle);
            }
      }
    }
    return;
}

// 格式化输出结果
void format_result( walk_list& res ) {

    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    WriteConsole(hStdOut, _T("["), 1, NULL, NULL);

    // 输出字符串
    auto f_print_str = [&](str& s)->void {
      WriteConsole(hStdOut, _T("\""), 1, NULL, NULL);
      WriteConsole(hStdOut, s.c_str(), s.length(), NULL, NULL);
      WriteConsole(hStdOut, _T("\""), 1, NULL, NULL);
    };

    // 输出列表元素
    auto f_print_list = [&](list<str>& v)->void {
      WriteConsole(hStdOut, _T("["), 1, NULL, NULL);
      for_each(v.begin(), v.end(),
            [&](str& s)->void{f_print_str(s);
            WriteConsole(hStdOut, _T(","), 1, NULL, NULL);});
      WriteConsole(hStdOut, _T("]"), 1, NULL, NULL);
    };

    // 输出元组元素
    auto f_print_tuple = [&](walk_list_element_type& v)->void {
      WriteConsole(hStdOut, _T("("), 1, NULL, NULL);
      // 输出目录
      f_print_str(std::get<0>(v));
      WriteConsole(hStdOut, _T(","), 1, NULL, NULL);
      // 输出子目录
      f_print_list(std::get<1>(v));
      WriteConsole(hStdOut, _T(","), 1, NULL, NULL);
      // 输出目录下的子文件
      f_print_list(std::get<2>(v));

      WriteConsole(hStdOut, _T(")"), 1, NULL, NULL);
    };
   
    // 遍历walk_list列表
    for_each(res.begin(), res.end(),
      [&](walk_list_element_type& v)->void{f_print_tuple(v);
      WriteConsole(hStdOut, _T(",\n"), 2, NULL, NULL);});

    WriteConsole(hStdOut, _T("]"), 1, NULL, NULL);

    return;
}

int main( int argc, char* argv[] ) {
    str note(_T("输入要遍历的目录(量力而行): "));
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), note.c_str(), note.length(), NULL, NULL);

    str path;
    get_input(path);

    // 规范输入的路径
    for ( auto it = path.begin(); it != path.end(); ++it) {
      if ( *it == '/' ) {
            *it = '\\';
      }
    }
    while ( path.back() == '\\' )
      path.pop_back();

    // 将"."转换成当前运行路径
    if ( path.length() == 1 && path == '.' ) {
      TCHAR buf;
      GetCurrentDirectory(MAX_PATH, buf);
      path = str(buf);
    }

    /// 用来接收结果
    walk_list rtn;

    walk(path, rtn, true);
    format_result(rtn);

    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), _T("\n"), 1, NULL, NULL);
    system("pause");

    return 0;
}



简单的测试,可绝对路径,相对路径



附上附件(源代码以及编译好的二进制可执行文件)

yntcxlong 发表于 2019-2-22 22:43

很不错,值得学习!
页: [1]
查看完整版本: [WIN32|STL]C++实现python的os模块的walk函数