代码之家  ›  专栏  ›  技术社区  ›  Adam Tegen

确定两个路径是否引用Windows中的同一文件的最佳方法?

  •  16
  • Adam Tegen  · 技术社区  · 15 年前

    我将如何比较2个字符串,以确定它们是否使用C/C++在Win32中引用相同的路径?

    虽然这将处理许多案件,但它遗漏了一些事情:

    _tcsicmp(szPath1, szPath2) == 0
    

    例如:

    • 正斜杠/反斜杠

    • 相对/绝对路径。

    [编辑]标题已更改,以匹配现有的C问题。

    11 回复  |  直到 6 年前
        1
  •  33
  •   MSN    15 年前

    打开两个文件 CreateFile ,呼叫 GetFileInformationByHandle 两者都有,比较一下 dwVolumeSerialNumber , nFileIndexLow , nFileIndexHigh . 如果三个都相等,则它们都指向同一个文件:

    GetFileInformationByHandle function

    BY_HANDLE_FILE_INFORMATION Structure

        2
  •  5
  •   Community Erin Dees    7 年前

    请参阅此问题: Best way to determine if two path reference to same file in C#

    问题是关于C,但答案只是win32 api调用 GetFileInformationByHandle .

        3
  •  5
  •   Gabe Timothy Khouri    13 年前

    使用kernel32.dll中的getfullpathname,这将为您提供文件的绝对路径。然后将它与使用简单字符串比较的其他路径进行比较

    编辑:代码

    TCHAR buffer1[1000];
    TCHAR buffer2[1000];
    TCHAR buffer3[1000];
    TCHAR buffer4[1000];
    
    GetFullPathName(TEXT("C:\\Temp\\..\\autoexec.bat"),1000,buffer1,NULL);
    GetFullPathName(TEXT("C:\\autoexec.bat"),1000,buffer2,NULL);
    GetFullPathName(TEXT("\\autoexec.bat"),1000,buffer3,NULL);
    GetFullPathName(TEXT("C:/autoexec.bat"),1000,buffer4,NULL);
    _tprintf(TEXT("Path1: %s\n"), buffer1);
    _tprintf(TEXT("Path2: %s\n"), buffer2);
    _tprintf(TEXT("Path3: %s\n"), buffer3);
    _tprintf(TEXT("Path4: %s\n"), buffer4);
    

    上面的代码将为所有三种路径表示打印相同的路径。你可能想在那之后做一个不区分大小写的搜索

        4
  •  3
  •   JaredPar    15 年前

    简单的字符串比较不足以比较路径是否相等。在Windows中,C:\foo\bar.txt和C:\temp\bar.txt很可能通过文件系统中的符号和硬链接指向完全相同的文件。

    正确地比较路径实际上迫使您打开两个文件并比较低级句柄信息。任何其他方法都会产生不稳定的结果。

    看看这篇关于这个主题的出色的博文。该代码是在VB中,但它是相当可移植的C/C++,因为他调用了大多数方法。

    http://blogs.msdn.com/vbteam/archive/2008/09/22/to-compare-two-filenames-lucian-wischik.aspx

        5
  •  3
  •   aldo    11 年前

    如果您可以访问Boost库,请尝试

    bool boost::filesystem::path::equivalent( const path& p1, const path& p2 )
    

    http://www.boost.org/doc/libs/1_53_0/libs/filesystem/doc/reference.html#equivalent

    从文档中总结:返回 true 如果给定 path 对象解析为同一文件系统实体,否则 false .

        6
  •  3
  •   Tim Diekmann suresh madaparthi    6 年前

    文件系统库

    因为C++ 17,你可以使用 standard filesystem library . 包括它使用 #include <filesystem> . 即使在较老版本的C++中,也可以访问它,请参阅脚注。

    你要找的功能是 equivalent ,在命名空间下 std::filesystem :

    bool std::filesystem::equivalent(const std::filesystem::path& p1, const filesystem::path& p2 );
    

    总结 documentation :此函数采用两条路径作为参数,如果它们引用同一文件或目录,则返回true,否则返回false。还有一个 noexcept 采用第三个参数的重载:一个 std::error_code 保存任何可能的错误。

    例子

    #include <filesystem>
    #include <iostream>
    //...
    
    int main() {
        std::filesystem::path p1 = ".";
        std::filesystem::path p2 = fs::current_path();
        std::cout << std::filesystem::equivalent(p1, p2));
        //...
    }
    

    输出:

    1
    

    在C++ 17之前使用文件系统

    若要在C++ 17之前的版本中使用此库,必须在编译器中启用实验语言特性,并以这种方式包含库: #include <experimental/filesystem> . 然后可以在命名空间下使用它的函数 std::experimental::filesystem . 请注意,实验文件系统库可能不同于C++ 17。参见文档 here .
    例如:

    #include <experimental/filesystem>
    //...
    std::experimental::filesystem::equivalent(p1, p2);
    
        7
  •  2
  •   Loki Astari    15 年前

    您需要做的是获得规范路径。

    对于每个路径,您都要求文件系统转换为规范路径,或者为您提供唯一标识文件的标识符(如inode)。

    然后比较规范路径或唯一标识符。

    注: 不要试着自己找出圆锥形路径文件系统可以用符号链接等做一些不容易处理的事情,除非您非常熟悉文件系统。

        8
  •  2
  •   Community Erin Dees    12 年前

    基于以下方面的答案 GetFileInformationByHandle() ,这是代码。

    注意:只有文件已经存在时,此操作才有效…

    //Determine if 2 paths point ot the same file...
    //Note: This only works if the file exists
    static bool IsSameFile(LPCWSTR szPath1, LPCWSTR szPath2)
    {
        //Validate the input
        _ASSERT(szPath1 != NULL);
        _ASSERT(szPath2 != NULL);
    
        //Get file handles
        HANDLE handle1 = ::CreateFileW(szPath1, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 
        HANDLE handle2 = ::CreateFileW(szPath2, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 
    
        bool bResult = false;
    
        //if we could open both paths...
        if (handle1 != INVALID_HANDLE_VALUE && handle2 != INVALID_HANDLE_VALUE)
        {
            BY_HANDLE_FILE_INFORMATION fileInfo1;
            BY_HANDLE_FILE_INFORMATION fileInfo2;
            if (::GetFileInformationByHandle(handle1, &fileInfo1) && ::GetFileInformationByHandle(handle2, &fileInfo2))
            {
                //the paths are the same if they refer to the same file (fileindex) on the same volume (volume serial number)
                bResult = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber &&
                          fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh &&
                          fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow;
            }
        }
    
        //free the handles
        if (handle1 != INVALID_HANDLE_VALUE )
        {
            ::CloseHandle(handle1);
        }
    
        if (handle2 != INVALID_HANDLE_VALUE )
        {
            ::CloseHandle(handle2);
        }
    
        //return the result
        return bResult;
    }
    
        9
  •  0
  •   user62572    15 年前

    如果引用UNC或规范路径(即除本地路径以外的任何路径),则比较实际路径字符串不会产生准确的结果。

    shlwapi.h有一些 Path Functions 如果你的道路是相同的,这可能会对你有用。

    它包含如下函数 PathIsRoot 可以在更大范围的函数中使用。

        10
  •  0
  •   CodeAngry    10 年前

    打开两个文件并使用 GetFinalPathNameByHandle() 反对 HANDLE 然后比较路径。

        11
  •  -1
  •   Matthew    15 年前

    如果文件存在,并且您可以处理打开文件时可能遇到的竞争条件和性能问题,那么在任何平台上都可以使用的一个不完美的解决方案是打开一个文件进行自己的写入,关闭它,然后在打开另一个文件进行写入后再次打开它进行写入。因为写访问权限只能是独占的,如果您能够第一次打开第一个文件进行写操作,而不是第二次打开,那么当您试图打开这两个文件时,很可能会阻止自己的请求。

    (当然,也有可能系统的其他部分打开了一个文件)