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

C++ 确定两个路径是否引用Windows中的同一文件的最佳方法?,c++,winapi,path,C++,Winapi,Path,在使用C/C++的Win32中,如何比较两个字符串以确定它们是否引用相同的路径 虽然这将处理很多情况,但它忽略了一些事情: _tcsicmp(szPath1, szPath2) == 0 例如: 正斜杠/反斜杠 相对/绝对路径 [编辑]更改标题以匹配现有的C#问题。简单的字符串比较不足以比较路径是否相等。在windows中,c:\foo\bar.txt和c:\temp\bar.txt很可能通过文件系统中的符号链接和硬链接指向完全相同的文件 正确地比较路径实际上迫使您打开这两个文件并比较低级

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

虽然这将处理很多情况,但它忽略了一些事情:

_tcsicmp(szPath1, szPath2) == 0
例如:

  • 正斜杠/反斜杠

  • 相对/绝对路径


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

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

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

看看这篇卢西恩关于这个主题的精彩文章。代码是用VB编写的,但它可以很好地翻译成C/C++语言,就像他对大多数方法所做的那样


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

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

看看这篇卢西恩关于这个主题的精彩文章。代码是用VB编写的,但它可以很好地翻译成C/C++语言,就像他对大多数方法所做的那样


使用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);

上述代码将为所有三种路径表示打印相同的路径。。在此之后,您可能需要执行不区分大小写的搜索

使用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);
上述代码将为所有三种路径表示打印相同的路径。。在此之后,您可能需要执行不区分大小写的搜索参见以下问题:

问题是关于C#,但答案只是Win32 API调用
GetFileInformationByHandle

请参见此问题:


问题是关于C#,但答案只是Win32 API调用
GetFileInformationByHandle

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

在确定路径是否相同时,shlwapi.h中有一些可能对您有用


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

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

在确定路径是否相同时,shlwapi.h中有一些可能对您有用


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

使用
CreateFile
打开两个文件,调用
GetFileInformationByHandle
,并比较
dwVolumeSerialNumber
nFileIndexLow
nFileIndexHigh
。如果三者都相等,则它们都指向同一个文件:


使用
CreateFile
打开这两个文件,调用这两个文件的
GetFileInformationByHandle
,然后比较
dwVolumeSerialNumber
nFileIndexLow
nFileIndexHigh
。如果三者都相等,则它们都指向同一个文件:

根据关于的答案,以下是代码

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

//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;
}
根据关于的答案,以下是代码

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

//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;
}

您需要做的是获取规范路径

对于您拥有的每个路径,请文件系统转换为规范路径,或为您提供唯一标识文件的标识符(例如iNode)

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

注:
不要试图自己找出锥形路径文件系统可以通过符号链接等进行操作,除非您非常熟悉文件系统,否则这些操作不容易处理。

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

对于您拥有的每个路径,请文件系统转换为规范路径,或为您提供唯一标识文件的标识符(例如iNode)

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

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

如果文件存在,并且您可以处理打开文件可能带来的争用情况和性能影响,在任何平台上都可以使用的一种不完美的解决方案是,打开一个文件进行单独写入,关闭它,然后在打开另一个文件进行写入后再次打开它进行写入。由于写入访问权限只能是独占的,如果您第一次可以打开第一个文件进行写入,但第二次不能,那么在尝试打开两个文件时,您可能会阻止自己的请求


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

如果文件存在,并且您可以处理打开文件可能带来的竞争条件和性能影响,那么在任何平台上都应该使用的一个不完美的解决方案是打开一个文件以供自己编写,然后关闭它,然后在打开另一个文件进行写入后再次打开该文件进行写入。因为写访问只允许被排除在外
#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
#include <experimental/filesystem>
//...
std::experimental::filesystem::equivalent(p1, p2);