C++ Windows上带有通配符的目录中的文件

C++ Windows上带有通配符的目录中的文件,c++,winapi,C++,Winapi,如何从包含通配符的路径轻松获取所有文件路径?例如:C:/Data*Set/Files*/*.txt,我在Linux上使用glob函数编写了它,但在Windows上无法做到: FindFirstFile不幸的是,它不支持目录名中的通配符 我认为应该有一个可用的Windows解决方案,但我找不到它。因此,您应该放弃使用特定于操作系统的文件访问,转而使用独立于操作系统的: 假设您得到了filesystem::path input,其中包含带有通配符的路径。要使用此解决您的问题,您需要: 用于将输入拆分

如何从包含通配符的路径轻松获取所有文件路径?例如:C:/Data*Set/Files*/*.txt,我在Linux上使用
glob
函数编写了它,但在Windows上无法做到:

FindFirstFile
不幸的是,它不支持目录名中的通配符


我认为应该有一个可用的Windows解决方案,但我找不到它。

因此,您应该放弃使用特定于操作系统的文件访问,转而使用独立于操作系统的:

假设您得到了
filesystem::path input
,其中包含带有通配符的路径。要使用此解决您的问题,您需要:

  • 用于将
    输入拆分为目录
  • 用于获取
    输入
    文件名
  • 输入开始的相对或绝对路径获取
  • 创建一个递归函数,该函数将
    begin
    end
    迭代器引入到获得的父路径、目录迭代器和文件名中
  • 每当目录或文件名使用
    “*”
    时,请使用带有迭代器的
    正则表达式来确定应前进到下一个目录
  • 返回匹配文件的路径或空的
    路径
  • 由于我已经更新了算法,可以跨过未签卡的目录

    例如:

    regex GenerateRegex(string& arg) {
        for (auto i = arg.find('*'); i != string::npos; i = arg.find('*', i + 2)) {
            arg.insert(i, 1, '.');
        }
    
        return regex(arg);
    }
    
    filesystem::path FindFirstFile(filesystem::path directory, filesystem::path::const_iterator& start, const filesystem::path::const_iterator& finish, string& filename) {
        while (start != finish && start->string().find('*') == string::npos) {
            directory /= *start++;
        }
        filesystem::directory_iterator it(directory);
        filesystem::path result;
    
        if (it != filesystem::directory_iterator()) {
            if (start == finish) {
                for (auto i = filename.find('.'); i != string::npos; i = filename.find('.', i + 2)) {
                    filename.insert(i, 1, '\\');
                }
                const auto re = GenerateRegex(filename);
    
                do {
                    if (!filesystem::is_directory(it->status()) && regex_match(it->path().string(), re)) {
                        result = *it;
                        break;
                    }
                } while (++it != filesystem::directory_iterator());
            }
            else {
                const auto re = GenerateRegex(start->string());
    
                do {
                    if (it->is_directory() && regex_match(prev(it->path().end())->string(), re)) {
                        result = FindFirstFile(it->path(), next(start), finish, filename);
    
                        if (!result.empty()) {
                            break;
                        }
                    }
                } while (++it != filesystem::directory_iterator());
            }
        }
        return result;
    }
    
    可通过以下方式调用:

    const filesystem::path input("C:/Test/Data*Set/Files*/*.txt");
    
    if (input.is_absolute()) {
        const auto relative_parent = input.parent_path().relative_path();
    
        cout << FindFirstFile(input.root_path(), begin(relative_parent), end(relative_parent), input.filename().string()) << endl;
    } else {
        const auto parent = input.parent_path();
    
        cout << FindFirstFile(filesystem::current_path(), begin(parent), end(parent), input.filename().string()) << endl;
    }
    
    const文件系统::路径输入(“C:/Test/Data*Set/Files*/*.txt”);
    if(input.is_absolute()){
    const auto relative_parent=input.parent_path().relative_path();
    
    cout需要了解
    FindFirstFile[Ex]
    是如何工作的。这已经结束。
    FindFirstFile[Ex]
    需要将输入名称分割为文件夹名称(将作为文件句柄打开),并将搜索掩码用作文件名。掩码只能在文件名中。文件夹必须具有准确的名称,且不带通配符才能首先打开

    因此,
    FindFirstFile[Ex]
    始终打开具体的单个文件夹,并通过掩码在此文件夹中搜索。对于递归搜索文件,我们需要递归调用
    FindFirstFile[Ex]
    。通常情况下,我们在所有级别上都使用相同的常量搜索掩码。例如,当我们希望查找所有文件时,都从
    X:\SomeFolder
    开始,我们首先调用
    FindFirstFile[Ex]
    并在级别0上使用
    X:\SomeFolder\*
    调用
    FindFirstFile[Ex]
    在级别1上使用
    X:\SomeFolder\SomeSubfolder\*
    等等。但是我们可以在不同级别上使用不同的搜索掩码。
    数据*集
    在级别0上,文件*
    在级别1上,在级别2上使用
    *.txt

    所以我们需要调用
    FindFirstFileEx
    recursive,并且在不同的递归级别上使用不同的掩码。例如,我们希望找到
    c:\Program*\*\*\*.txt
    。我们需要从
    c:\Program*
    开始,然后为每个创建的结果追加
    \*
    掩码,然后在下一级别追加
    \*.txt
    。或者我们可以例如want next-按下一个掩码搜索文件-
    c:\Program files*\Internet Explorer\*
    具有任何深度级别。我们可以使用恒定深度搜索文件夹掩码(可选),最终掩码(也可选)已在所有更深级别上使用。 所有这一切其实都不那么困难和高效:

    struct ENUM_CONTEXT : WIN32_FIND_DATA 
    {
        PCWSTR _szMask;
        PCWSTR *_pszMask;
        ULONG _MaskCount;
        ULONG _MaxLevel;
        ULONG _nFiles;
        ULONG _nFolders;
        WCHAR _FileName[MAXSHORT + 1];
    
        void StartEnum(PCWSTR pcszRoot, PCWSTR pszMask[], ULONG MaskCount, PCWSTR szMask, ULONG MaxLevel, PSTR prefix)
        {
            SIZE_T len = wcslen(pcszRoot);
    
            if (len < RTL_NUMBER_OF(_FileName))
            {
                memcpy(_FileName, pcszRoot, len * sizeof(WCHAR));
    
                _szMask = szMask, _pszMask = pszMask, _MaskCount = MaskCount;
                _MaxLevel = szMask ? MaxLevel : MaskCount;
                _nFolders = 0, _nFolders = 0;
    
                Enum(_FileName + len, 0, prefix);
            }
        }
    
        void Enum(PWSTR pszEnd, ULONG nLevel, PSTR prefix);
    };
    
    void ENUM_CONTEXT::Enum(PWSTR pszEnd, ULONG nLevel, PSTR prefix)
    {
        if (nLevel > _MaxLevel)
        {
            return ;
        }
    
        PCWSTR lpFileName = _FileName;
    
        SIZE_T cb = lpFileName + RTL_NUMBER_OF(_FileName) - pszEnd;
    
        PCWSTR szMask = nLevel < _MaskCount ? _pszMask[nLevel] : _szMask;
    
        SIZE_T cchMask = wcslen(szMask) + 1;
    
        if (cb < cchMask + 1)
        {
            return ;
        }
    
        *pszEnd++ = L'\\', cb--;
    
        DbgPrint("%s[<%.*S>]\n", prefix, pszEnd - lpFileName, lpFileName);
    
        memcpy(pszEnd, szMask, cchMask * sizeof(WCHAR));
    
        ULONG dwError;
    
        HANDLE hFindFile = FindFirstFileEx(lpFileName, FindExInfoBasic, this, FindExSearchNameMatch, 0, FIND_FIRST_EX_LARGE_FETCH);
    
        if (hFindFile != INVALID_HANDLE_VALUE)
        {
            PWSTR FileName = cFileName;
    
            do 
            {
                SIZE_T FileNameLength = wcslen(FileName);
    
                switch (FileNameLength)
                {
                case 2:
                    if (FileName[1] != '.') break;
                case 1:
                    if (FileName[0] == '.') continue;
                }
    
                if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                {
                    _nFolders++;
    
                    if (!(dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
                    {
                        if (cb < FileNameLength)
                        {
                            __debugbreak();
                        }
                        else
                        {
                            memcpy(pszEnd, FileName, FileNameLength * sizeof(WCHAR));
                            Enum(pszEnd + FileNameLength, nLevel + 1, prefix - 1);
                        }
                    }
                }
                else if (nLevel >= _MaskCount || (!_szMask && nLevel == _MaskCount - 1))
                {
                    _nFiles++;
    
                    DbgPrint("%s%u%u <%.*S>\n", prefix, nFileSizeLow, nFileSizeHigh, FileNameLength, FileName);
                }
    
            } while (FindNextFile(hFindFile, this));
    
            if ((dwError = GetLastError()) == ERROR_NO_MORE_FILES)
            {
                dwError = NOERROR;
            }
    
            FindClose(hFindFile);
        }
        else
        {
            dwError = GetLastError();
        }
    
        if (dwError && dwError != ERROR_FILE_NOT_FOUND)
        {
            DbgPrint("%s[<%.*S>] err = %u\n", prefix, pszEnd - lpFileName, lpFileName, dwError);
        }
    }
    
    void Test(PCWSTR pcszRoot)
    {
        char prefix[MAXUCHAR + 1];
        memset(prefix, '\t', RTL_NUMBER_OF(prefix) - 1);
        prefix[RTL_NUMBER_OF(prefix) - 1] = 0;
    
        ENUM_CONTEXT ectx;
    
        static PCWSTR Masks[] = { L"Program*", L"*", L"*.txt" };
        static PCWSTR Masks2[] = { L"Program*", L"*" };
        static PCWSTR Masks3[] = { L"Program Files*", L"Internet Explorer" };
    
        // search Program*\*\*.txt with fixed deep level
        ectx.StartEnum(pcszRoot, Masks, RTL_NUMBER_OF(Masks), 0, RTL_NUMBER_OF(prefix) - 1, prefix + RTL_NUMBER_OF(prefix) - 1);
        // search *.txt files from Program*\*\ - any deep level
        ectx.StartEnum(pcszRoot, Masks2, RTL_NUMBER_OF(Masks2), L"*.txt", RTL_NUMBER_OF(prefix) - 1, prefix + RTL_NUMBER_OF(prefix) - 1);
        // search all files (*) from Program Files*\Internet Explorer\ 
        ectx.StartEnum(pcszRoot, Masks3, RTL_NUMBER_OF(Masks3), L"*", RTL_NUMBER_OF(prefix) - 1, prefix + RTL_NUMBER_OF(prefix) - 1);
    
    }
    
    结构枚举上下文:WIN32查找数据 { PCWSTR_szMask; PCWSTR*_pszMask; 乌龙马斯克山; ULONG_MaxLevel; 乌龙文件; 乌龙文件夹; WCHAR_文件名[MAXSHORT+1]; void StartEnum(PCWSTR pcszRoot,PCWSTR pszMask[],ULONG MaskCount,PCWSTR szMask,ULONG MaxLevel,PSTR前缀) { 尺寸长度=wcslen(pcszRoot); if(len\u MaxLevel) { 返回; } PCWSTR lpFileName=\u文件名; 大小\u T cb=lpFileName+RTL\u编号\u的(\u FileName)-pszEnd; PCWSTR szMask=nLevel<\uMaskCount?\uPSZMASK[nLevel]:\uSZMASK; 大小\u T cchMask=wcslen(szMask)+1; 如果(cb=|MaskCount |(!_szMask&&nLevel==|MaskCount-1)) { _nFiles++; DbgPrint(“%s%u%u\n”,前缀,nFileSizeLow,nFileSizeHigh,FileNameLength,FileName);