在Windows上获得路径的有效方法,就像在文件系统中一样

时间:2014-06-12 17:19:26

标签: winapi filesystems

Windows文件系统不区分大小写。当我有一个文件路径时,我希望这些案例与文件系统中的情况完全一样。现在我使用以下代码来确保这一点:

std::vector<std::wstring> directories;
boost::split(directories, api, is_any_delimiter);

size_t i = 0;
std::wstring path;

if (hasWindowsDriverLetter())
{
    path += directories[i++];
}

std::wstring delimiter(L"\\");

for (;i < directories.size(); ++i)
{
    std::wstring search = path + delimiter + directories[i];

    WIN32_FIND_DATAW FindFileData;
    HANDLE hFind = FindFirstFileW(search.c_str(), &FindFileData);
    if (hFind == INVALID_HANDLE_VALUE)
    {
        path = search;
        continue;
    }

    path += delimiter + FindFileData.cFileName;
    ::FindClose(hFind);
}

我的功能在性能敏感的地方 - 有更有效的方法吗?

注意:GetLongPathNameW不起作用。

1 个答案:

答案 0 :(得分:0)

尝试这样的事情:

std::wstring::iterator GetIteratorAfterRoot(std::wstring &str)
{
    std::wstring::iterator iter = str.begin();

    wchar_t *tmp = str.c_str();
    wchar_t *tmp2;
    if (PathCchSkipRoot(tmp, &tmp2) == S_OK)
        iter += std::distance(tmp, tmp2);

    return iter;
}

std::wstring api = ...;

std::wstring start = GetIteratorAfterRoot(api);
std::wstring end = api.end();

WIN32_FIND_DATAW FindFileData;
HANDLE hFind;

bool finished = false;
while (start != end)
{
    std::wstring::iterator found = std::find_if(start, end, is_any_delimiter); 

    if (found != end)
    {
        wchar_t delim = *found;
        *found = L'\0';
        hFind = FindFirstFileW(api.c_str(), &FindFileData);
        *found = delim;
    }
    else
    {
        finished = true;
        hFind = FindFirstFileW(api.c_str(), &FindFileData);
    }

    if (hFind != INVALID_HANDLE_VALUE)
    {
        ::FindClose(hFind);

        std::wstring::difference_type SearchLength = std::distance(start, found);
        int FileNameLength = lstrlenW(FindFileData.cFileName);

        if (SearchLength != FileNameLength)
        {
            // string length is changing...

            if (finished)
            {
                api.replace(start, found, FindFileData.cFileName);
                break;
            }

            std::wstring::difference_type SearchOffset = std::distance(api.begin(), start);
            api.replace(start, found, FindFileData.cFileName);
            start = api.begin() + (SearchOffset + FileNameLength + 1);
            end = api.end();

            continue;
        }

        // string length not changing, copy new name inlined...

        // not sure if std::wstring::replace() is smart enough to do an
        // inline copy if the two lengths match, so using std::copy instead...

        //api.replace(start, found, FindFileData.cFileName); 
        std::copy(FindFileData.cFileName, &FindFileData.cFileName[FileNameLength], start); 
    }

    if (finished)
        break;

    start = found + 1;
}