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

时间:2009-02-18 20:32:25

标签: c++ winapi path

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

虽然这会处理很多情况,但它会遗漏一些事情:

_tcsicmp(szPath1, szPath2) == 0

例如:

  • 正斜杠/反斜杠

  • 相对/绝对路径。

[编辑]标题已更改为与现有的C#问题相匹配。

11 个答案:

答案 0 :(得分:33)

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

GetFileInformationByHandle function

BY_HANDLE_FILE_INFORMATION Structure

答案 1 :(得分:5)

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

上面的代码将为所有三个路径表示打印相同的路径..您可能希望在此之后进行不区分大小写的搜索

答案 2 :(得分:5)

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

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

答案 3 :(得分:3)

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

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

查看Lucian在这个主题上发表的这篇优秀文章。代码在VB中,但它可以很好地转换为C / C ++,因为他对大多数方法进行了PInvoke。

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

答案 4 :(得分:3)

如果您有权访问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

答案 5 :(得分:3)

文件系统库

从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);

答案 6 :(得分:2)

根据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;
}

答案 7 :(得分:2)

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

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

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

注意: 不要试图自己弄清楚锥形路径,文件系统可以用符号链接等做一些不容易处理的事情,除非你对文件系统非常熟悉。

答案 8 :(得分:0)

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

shlwapi.h有一些Path Functions可能对您有用,可以确定您的路径是否相同。

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

答案 9 :(得分:0)

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

答案 10 :(得分:-1)

如果文件存在且您可以处理潜在的竞争条件和打开文件所带来的性能,那么应该在任何平台上运行的不完美解决方案是打开一个文件以便自行编写,关闭它,然后打开它打开另一个文件写入后再次写入。由于只允许写入访问是独占的,如果您能够第一次打开第一个文件而不是第二次写入,那么当您尝试打开这两个文件时,您可能会阻止自己的请求。

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