开发者

Testing equivalence of file paths

开发者 https://www.devze.com 2023-03-19 09:39 出处:网络
There are two strings in a progr开发者_运维知识库am. Each of them contains a path to some file or folder. How can I check in C++ whether these paths are to the same file/folder? Can I use the Windows

There are two strings in a progr开发者_运维知识库am. Each of them contains a path to some file or folder. How can I check in C++ whether these paths are to the same file/folder? Can I use the Windows API to do this?


You might use Boost Filesystem.

It has the added weight of being cross-platform; this is obviously also a potential advantage. Note the bolded API reference below in case you want to check it out: GetFileInformationByHandle.

equivalent

bool equivalent(const path& p1, const path& p2);
bool equivalent(const path& p1, const path& p2, system::error_code& ec);

Effects: Determines file_status s1 and s2, as if by status(p1) and status(p2), respectively.

Returns: true, if sf1 == sf2 and p1 and p2 resolve to the same file system entity, else false.

Two paths are considered to resolve to the same file system entity if two candidate entities reside on the same device at the same location. This is determined as if by the values of the POSIX stat structure, obtained as if by stat() for the two paths, having equal st_dev values and equal st_ino values.

[Note: POSIX requires that "st_dev must be unique within a Local Area Network". Conservative POSIX implementations may also wish to check for equal st_size and st_mtime values. Windows implementations may use GetFileInformationByHandle() as a surrogate for stat(), and consider "same" to be equal values for dwVolumeSerialNumber, nFileIndexHigh, nFileIndexLow, nFileSizeHigh, nFileSizeLow, ftLastWriteTime.dwLowDateTime, and ftLastWriteTime.dwHighDateTime. -- end note]

Throws: filesystem_error if (!exists(s1) && !exists(s2)) || (is_other(s1) && is_other(s2)), otherwise as specified in Error reporting.


Check out GetFullPathName: http://msdn.microsoft.com/en-us/library/aa364963(v=vs.85).aspx


You might be looking for GetFinalPathNameByHandle(hFile, outPath, outSize, FILE_NAME_NORMALIZED | VOLUME_NAME_GUID)


Convert the path strings to PIDLs using IShellFolder::ParseDisplayName(), SHParseDisplayName(), or ILCreateFromPath(), then compare the PIDLs to each other using IShellFolder::CompareIDs() or ILIsEqual().


Based on Remy Lebeau's answer I came up with this:
It handles many cases like environmental variables, network drives and subst mapping (even when it maps to \\ServerName\Folder), but I'm sure there are cases that I have not thought of.

typedef std::wstring String;
void FixPath(const String& aPath, wchar_t* pBuff, DWORD nBuffSize)
{
    wchar_t* canoPath = nullptr;
    bool hasBeenSet = false;

    ::ZeroMemory(pBuff, sizeof(wchar_t) * nBuffSize);
    ::ExpandEnvironmentStrings(aPath.c_str(), pBuff, nBuffSize);
    ::PathAllocCanonicalize(pBuff, PATHCCH_ALLOW_LONG_PATHS, &canoPath);
    ::ZeroMemory(pBuff, sizeof(wchar_t) * nBuffSize);
    if ((2 < wcslen(canoPath)) && (L':' == canoPath[1]))
    {
        wchar_t devName[] = { canoPath[0], L':', L'\0' };
        wchar_t targetPath[MAX_PATH];
        DWORD len = ARRAYSIZE(targetPath);
        wchar_t* p = nullptr;

        ::ZeroMemory(targetPath, sizeof(targetPath));
        if (NO_ERROR == ::WNetGetConnection(devName, targetPath, &len))
            p = targetPath;
        else
        {
            len = ::QueryDosDevice(devName, targetPath, ARRAYSIZE(targetPath)); // Check for subst mapping
            if ((6 < len) && (0 == _wcsnicmp(targetPath, L"\\??\\", 4)))
            {
                p = &targetPath[4];
                if (0 == _wcsnicmp(p, L"UNC", 3)) // if subst was used to directly map to \\serverName\folder
                {
                    p = p + 2;
                    *p = L'\\';
                }
            }
        }
        if(nullptr != p)
        {
            wchar_t* s = &canoPath[2];

            if (L'\\' == *s)
                s++;
            if (L'\0' == *s) // if the passed in path was just the drive letter (Dos Device) and that letter was a subst to another location, there will be nothing is s to append
                ::StringCchCopy(pBuff, nBuffSize, p);
            else
            {
                ::PathCchCombine(pBuff, nBuffSize, p, s);
                ::GetLongPathName(pBuff, pBuff, nBuffSize);
            }
            hasBeenSet = true;
        }
    }
    if (!hasBeenSet)
        ::GetLongPathName(canoPath, pBuff, nBuffSize);
    ::LocalFree(canoPath);
}

#define ILFreeEx(pIdl)\
    if(nullptr != pIdl)\
    {\
        ::ILFree(pIdl);\
        pIdl = nullptr;\
    }

bool SamePath(const String& pathA, const String& pathB)
{
    wchar_t fullPathA[2048];
    wchar_t fullPathB[2048];
    PIDLIST_ABSOLUTE pIdlA = nullptr;
    PIDLIST_ABSOLUTE pIdlB = nullptr;
    bool retVal = false;
    
    FixPath(pathA, fullPathA, ARRAYSIZE(fullPathA));
    FixPath(pathB, fullPathB, ARRAYSIZE(fullPathB));
    pIdlA = ::ILCreateFromPath(fullPathA);
    pIdlB = ::ILCreateFromPath(fullPathB);
    if ((nullptr != pIdlA) && (nullptr != pIdlB))
        retVal = ::ILIsEqual(pIdlA, pIdlB);
    else
        retVal = (0 == _wcsicmp(fullPathA, fullPathB));
    ILFreeEx(pIdlA);
    ILFreeEx(pIdlB);
    return retVal;
}


You can use substr of the String library. First, you have to put #include for call substr correctly. Look at this link: http://www.cplusplus.com/reference/string/string/substr/

Good luck

0

精彩评论

暂无评论...
验证码 换一张
取 消