【发布时间】:2010-10-08 10:36:23
【问题描述】:
如何比较两个字符串以确定它们是否使用 C/C++ 在 Win32 中引用相同的路径?
虽然这会处理很多情况,但它会遗漏一些事情:
_tcsicmp(szPath1, szPath2) == 0
例如:
正斜杠/反斜杠
相对/绝对路径。
[编辑] 标题已更改以匹配现有 C# 问题。
【问题讨论】:
如何比较两个字符串以确定它们是否使用 C/C++ 在 Win32 中引用相同的路径?
虽然这会处理很多情况,但它会遗漏一些事情:
_tcsicmp(szPath1, szPath2) == 0
例如:
正斜杠/反斜杠
相对/绝对路径。
[编辑] 标题已更改以匹配现有 C# 问题。
【问题讨论】:
使用CreateFile 打开两个文件,调用GetFileInformationByHandle 并比较dwVolumeSerialNumber、nFileIndexLow、nFileIndexHigh。如果三个都相等,则它们都指向同一个文件:
【讨论】:
从 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 之前的版本中使用此库,您必须在编译器中启用实验性语言功能并以这种方式包含该库:#include <experimental/filesystem>。然后,您可以在命名空间std::experimental::filesystem 下使用它的功能。请注意,实验文件系统库可能与 C++17 不同。请参阅文档here。
例如:
#include <experimental/filesystem>
//...
std::experimental::filesystem::equivalent(p1, p2);
【讨论】:
看到这个问题:Best way to determine if two path reference to same file in C#
问题是关于 C#,但答案只是 Win32 API 调用GetFileInformationByHandle。
【讨论】:
使用 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);
上面的代码将为所有三个路径表示打印相同的路径.. 之后您可能希望进行不区分大小写的搜索
【讨论】:
简单的字符串比较不足以比较路径是否相等。在 Windows 中,c:\foo\bar.txt 和 c:\temp\bar.txt 很可能通过文件系统中的符号链接和硬链接指向完全相同的文件。
正确比较路径本质上会强制您打开两个文件并比较低级句柄信息。任何其他方法都会产生不稳定的结果。
查看 Lucian 就该主题发表的这篇出色的帖子。该代码在 VB 中,但由于他 PInvoke 了大多数方法,因此可以很好地转换为 C/C++。
http://blogs.msdn.com/vbteam/archive/2008/09/22/to-compare-two-filenames-lucian-wischik.aspx
【讨论】:
根据关于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;
}
【讨论】:
GetFileInformationByHandle,并且此代码不会在调用之间关闭任何句柄。
如果您可以访问 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
从文档中总结:如果给定的 path 对象解析为相同的文件系统实体,则返回 true,否则返回 false。
【讨论】:
您需要做的是获取规范路径。
对于您要求文件系统转换为规范路径或为您提供唯一标识文件的标识符(例如 iNode)的每个路径。
然后比较规范路径或唯一标识符。
注意: 不要尝试自己找出锥形路径,文件系统可以使用符号链接等做一些不容易处理的事情,除非您非常熟悉文件系统。
【讨论】:
如果您引用 UNC 或规范路径(即本地路径以外的任何路径),比较实际路径字符串将不会产生准确的结果。
shlwapi.h 有一些 Path Functions 可能对您确定您的路径是否相同有用。
它包含像PathIsRoot 这样的函数,可以在更大范围的函数中使用。
【讨论】:
如果文件存在并且您可以处理打开文件可能导致的竞争条件和性能损失,那么适用于任何平台的不完美解决方案是打开一个文件自行写入,关闭它,然后再打开它打开另一个文件进行写入后再次写入。由于只允许独占写入权限,因此如果您能够打开第一个文件以进行第一次写入但第二次无法打开,那么当您尝试打开这两个文件时,您可能会阻止您自己的请求。
(当然,也有可能是系统的其他部分打开了您的文件之一)
【讨论】:
打开这两个文件并使用 GetFinalPathNameByHandle() 反对HANDLEs。然后比较路径。
【讨论】: