huidong

首页 | 会员登录 | 关于争取 2022 寒假做出汇东网 Ver3.0.0 !
搜索文章



如题。


之前我也写过类似功能的函数,但是效果还不够好,不能一次性满足所有需求。

见:http://huidong.xyz/?mode=2&id=195


于是我又上网找了一下,看到了这个:https://blog.csdn.net/YhL_Leo/article/details/51009608

它的亮点是用了一下递归把子文件夹内的文件也找出来了。打开文章里说的github路径,可以看到它的实现:https://github.com/yhlleo/FindFilesWithinFolder/blob/master/FindFiles.cpp


他还有个亮点是把linux下的也做出来了,不过不是重点。


但是他弄了单独的两个文件写声明和实现,而我只要一个函数就够了。

所以我把他的函数提取了出来,但是后来我发现他的函数做的不够好:他一定会搜索子文件夹内的所有文件,我希望可以更细致地调整参数,所以对他的函数进行了修改。


修改内容:

  1. 路径支持斜杠('/')和反斜杠('\')

  2. 可选地查找子文件夹中的内容

  3. 可选地返回子文件夹的目录路径

  4. 将返回值的vector改为了wstring*(个人喜好)


函数实现和调用示例:

#include <windows.h>
#include <string>
#include <vector>
using namespace std;

//
// FindFiles
// 寻找目录下的文件,返回满足条件的文件列表
// 
// lpPath 目标目录路径(路径末尾可以带'\'或者'/',也可以不带)
// bGetChildFolder 返回的文件列表中是否包含子文件夹本身的目录路径(只有当secName为".*"时才能正常返回子文件夹目录路径)
// bGetChildFolderFiles 返回的文件列表中是否包含子文件夹中的文件
// p_wstrFilesList 传入一个空指针的地址,将得到和返回值相同的内容。
// pFilesNum 得到查找到的文件数量
// secName 要查找的文件的后缀名要求,".*"表示所有后缀名。只支持填写一个后缀名,且需要带上点('.')。
// recursion_flag 递归标志。标志当前函数是否在递归中,用户调用此函数时请保持此值为false
// 
// 注意:如果返回的文件列表中含有子文件夹路径,它们都会以'\'结尾,比如 "D:\new_folder\"
//
wstring* FindFiles(const wchar_t* lpPath, bool bGetChildFolder, bool bGetChildFolderFiles, wstring** p_wstrFilesList, int* pFilesNum, const wchar_t* secName = L".*", bool inner_flag = false)
{
    wchar_t szFind[MAX_PATH] = { 0 };
    wchar_t szFile[MAX_PATH] = { 0 };
    wchar_t szPath[MAX_PATH] = { 0 };

    WIN32_FIND_DATA FindFileData;
    static vector<wstring> files_list;

    // '/' 全部改 '\'
    for (int i = 0; i < (int)lstrlen(lpPath); i++)
    {
        if (lpPath[i] == L'/')
        {
            szPath[i] = L'\\';
        }
        else
        {
            szPath[i] = lpPath[i];
        }
    }

    lstrcpy(szFind, szPath);

    // 末尾不含'\'时,带上'\'
    if (szFind[lstrlen(szFind) - 1] != '\\')
        lstrcat(szFind, L"\\");

    lstrcat(szFind, L"*");
    lstrcat(szFind, secName);

    // 如果有文件后缀名要求,且需要得到子文件夹中的文件
    // 如果限制后缀,其实是无法得到文件目录的,所以需要单独获取文件子目录
    if (bGetChildFolderFiles && lstrcmp(secName, L".*") != 0)
    {
        // 得到参数目录下的所有文件(含子文件夹内的文件)
        wstring* p_wstrAllFilesList = NULL;
        int nAllFilesNum = 0;
        FindFiles(szPath, false, true, &p_wstrAllFilesList, &nAllFilesNum);

        // 遍历所有文件,查找符合后缀的文件
        for (int i = 0; i < nAllFilesNum; i++)
        {
            // 找到此文件路径的最后一个点('.')的位置
            int nLastPointIndex = 0;
            for (int j = 0; j < (int)p_wstrAllFilesList[i].size(); j++)
                if (p_wstrAllFilesList[i][j] == L'.')
                    nLastPointIndex = j;
            // 后缀名一致,存入vector
            if (lstrcmp(&p_wstrAllFilesList[i][nLastPointIndex], secName) == 0)
            {
                files_list.push_back(p_wstrAllFilesList[i]);
            }
        }
    }

    // 如果没有上述要求,则正常进行读取
    else
    {
        HANDLE hFind = ::FindFirstFile(szFind, &FindFileData);

        // 空目录
        if (INVALID_HANDLE_VALUE == hFind)
        {
            return NULL;
        }

        do
        {
            if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                // 是文件夹
                if (FindFileData.cFileName[0] != '.')
                {
                    lstrcpy(szFile, szPath);

                    if (szFile[lstrlen(szFile) - 1] != '\\')
                        lstrcat(szFile, L"\\");

                    lstrcat(szFile, FindFileData.cFileName);
                    lstrcat(szFile, L"\\");

                    if (bGetChildFolder)
                        files_list.push_back(szFile);

                    if (bGetChildFolderFiles)
                        FindFiles(szFile, bGetChildFolder, bGetChildFolderFiles, p_wstrFilesList, pFilesNum, secName, true);
                }
            }
            else
            {
                if (szFile[0])
                {
                    wstring filePath = szPath;
                    if (szPath[lstrlen(szPath) - 1] != '\\')
                        filePath += L"\\";
                    filePath += FindFileData.cFileName;
                    files_list.push_back(filePath);
                }
                else
                {
                    if(lstrlen(szFile) == 0)
                        lstrcpy(szFile, szPath);
                    wstring filePath = szFile;
                    filePath += FindFileData.cFileName;
                    files_list.push_back(filePath);
                }
            }

        } while (::FindNextFile(hFind, &FindFileData));

        ::FindClose(hFind);
    }

    *pFilesNum = files_list.size();
    if (!inner_flag)
    {
        *p_wstrFilesList = new wstring[*pFilesNum];
        for (int i = 0; i < (int)files_list.size(); i++)
        {
            (*p_wstrFilesList)[i] = files_list[i];
        }
        files_list.clear();
    }

    return *p_wstrFilesList;
}

int main()
{
    // 存储文件列表行数
    int num = 0;
    // 存储文件列表
    wstring* wstr_pFiles = FindFiles(L"E:\\_MYC\\_VS_Project", false, true, &wstr_pFiles, &num, L".*");

    // 打印文件列表
    for (int i = 0; i < num; i++)
        wprintf(L"%ls\n", wstr_pFiles[i].c_str());

    // 输出文件列表行数
    printf("\n\n%d\n\n",num);

    return 0;
}


上面这个函数是多次测试通过的,没有bug。

原本这个函数写好之后马上就发了这个文章,后来实际应用中出现了bug,就是在有后缀名限定时,无法获取子文件夹中符合条件的文件,原因是如果限制了文件后缀名,就无法得到子文件夹的目录路径,从而无法得到子文件夹中的文件路径。解决方法就是判断当需要寻找子文件夹中的文件并且有后缀名限定时,递归运行一次没有后缀要求的FindFiles函数,得到目录下的所有文件,再手动判断这些文件是否符合后缀要求,最后返回给用户。

后来,在7.13的时候切换为release编译,发现这个函数在release下出现bug,又折腾了一波,改了几个地方,现在确实是没有什么Bug了。


效果:

图片.png




返回首页


Copyright (C) 2018-2022 huidong