【问题标题】:Searching files filtering by extension returns too many results搜索按扩展名过滤的文件返回太多结果
【发布时间】:2017-12-08 19:28:09
【问题描述】:

我正在开发一个必须在 Windows 操作系统上管理文件的 C++ 控制台应用程序。我需要获取具有特定扩展名的文件名的“列表”。我找到了很多解决方案,建议最多的是以下一种:

HANDLE hFind;
WIN32_FIND_DATA data;

hFind = FindFirstFile("C:\\PWS\\*.sda", &data);
if (hFind != INVALID_HANDLE_VALUE) {
    do {
        cout << data.cFileName << endl;

    } while (FindNextFile(hFind, &data));
    FindClose(hFind);
}

假设我在C:\\PWS 文件夹中有这些文件:

  • f001.sdac
  • f002.sda
  • f003.sdab
  • f004.sda

上面的代码打印了所有这些,而我只需要f002.sdaf004.sda

有什么提示吗?

NB: 我不想使用 boost 库。

【问题讨论】:

  • ...但是你可以使用 C++ 的文件系统库。
  • 您是否在 cmd 上尝试过dir *.sda 以查看结果。它认为这是 Windows 通配符的问题
  • This answer 表明原因是 Windows 寻找短 8.3 文件名和长文件名的匹配项。找到一种方法为您的进程禁用此行为或使用FindFirstFile("C:\\PWS\\*") 并自行执行文件扩展名过滤,例如使用PathMatchSpec() 函数,该函数仅使用您传递给它的字符串并且不会检查 8.3 名称.
  • @k-5,给你点!

标签: c++ file winapi


【解决方案1】:

代码还会找到 "f001.sdac""f003.sdab",因为 FindFirstFile()FindFirstFileEx() 匹配短 (8.3) 和长文件名。例如,"f001.sdac" 的短文件名可能类似于 "f~1.sda"

原因是backwards compatibility with 16-bit (sigh!) programs

要解决此问题,请使用* 通配符调用FindFirstFile() 以匹配所有文件,然后执行您自己的过滤,例如调用PathMatchSpec() 函数。 PathMatchSpec() 只进行字符串匹配,因此它没有 FindFirstFile() 的奇怪行为。

WIN32_FIND_DATAW data;    
HANDLE hFind = FindFirstFileW( L"C:\\PWS\\*", &data );
if( hFind != INVALID_HANDLE_VALUE ) 
{
    do 
    {
        if( ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == 0 && 
            PathMatchSpecW( data.cFileName, L"*.sda" ) )
        {
            std::wcout << data.cFileName << std::endl;
        }
    } 
    while( FindNextFileW( hFind, &data ) );

    FindClose( hFind );
}

旁注:使用FindExInfoBasic 的值调用FindFirstFileEx() 以获取fInfoLevelId 参数,这应该是“不查询短文件名”,不是此问题的有效解决方案,因为它仍将匹配短 (8.3) 文件名。

【讨论】:

  • 短语“不查询短文件名”只是表示返回的数据中不包含短文件名。它不应该改变搜索的执行方式。
【解决方案2】:

这样的东西应该为你过滤结果:

bool hasFileExtension(TCHAR cFileName[], TCHAR* ptcExtension)
{
    bool result = true;
    int iFileNameLength = _tcslen(cFileName);
    int iExtensionLength = _tcslen(ptcExtension);

    if (iFileNameLength >= iExtensionLength)
    {
        for (int i = 1; i < iExtensionLength + 1 && result; i++)
        {
            if (cFileName[iFileNameLength - i] != ptcExtension[iExtensionLength - i])
            {
                result = false;
            }
        }
    }
    else
    {
        result = false;
    }

    return result;
}

void listFilesWithExtension(LPCTSTR lpFileName, TCHAR* ptcExtension)
{
    HANDLE hFind;
    WIN32_FIND_DATA data;

    hFind = FindFirstFile(lpFileName, &data);
    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            if (hasFileExtension(data.cFileName, ptcExtension))
            {
                wcout << data.cFileName << endl;
            }
        } while (FindNextFile(hFind, &data));
        FindClose(hFind);
    }
}

int main()
{
    LPCTSTR lpFileName = L"C:\\PWS\\*.sda";
    TCHAR* ptcExtension = _T(".sda");
    listFilesWithExtension(lpFileName, ptcExtension);
    Sleep(5000);
    return 0;
}

【讨论】:

    【解决方案3】:

    我采用的解决方案如下:

    void GetFilesByNameRootAndExtension(const string& dirPath, const string& nameRoot, string& ext, vector<string>& files)
    {
        files.clear();
    
        stringstream ss;
        ss << dirPath << "\\" << nameRoot << "*" << ext;
        string searchKeyS = ss.str();
        const char* searchKey = searchKeyS.c_str();
    
        WIN32_FIND_DATA data;
        HANDLE hFind = FindFirstFile(searchKey, &data);
    
        ext = ext.erase(0, 1);
        if (hFind != INVALID_HANDLE_VALUE) {
            do {
                string fN = data.cFileName;
                if (fN.substr(fN.find_last_of(".") + 1) == ext) // filtering by extension
                    files.push_back(fN);
            } while (FindNextFile(hFind, &data));
            FindClose(hFind);
        }
    }
    

    谢谢大家的提示!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-11
      • 1970-01-01
      • 1970-01-01
      • 2018-12-10
      • 1970-01-01
      • 1970-01-01
      • 2019-09-12
      • 2016-05-03
      相关资源
      最近更新 更多