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

问题描述 投票:16回答:11

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

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

_tcsicmp(szPath1, szPath2) == 0

例如:

  • 正斜杠/反斜杠
  • 相对/绝对路径。

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

c++ winapi path
11个回答
33
投票

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

GetFileInformationByHandle function

BY_HANDLE_FILE_INFORMATION Structure


0
投票

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


-1
投票

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

(当然,机会的其他部分也有可能打开你的一个文件)


5
投票

看到这个问题:Best way to determine if two path reference to same file in C#

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


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

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


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


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


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

2
投票

你需要做的是获得规范的道路。

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

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

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


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

0
投票

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

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

它包含像PathIsRoot这样的函数,可以在更大范围的函数中使用。

© www.soinside.com 2019 - 2024. All rights reserved.