Ответ 1
Откройте оба файла с помощью CreateFile
, вызовите GetFileInformationByHandle
для обоих и сравните dwVolumeSerialNumber
, nFileIndexLow
, nFileIndexHigh
. Если все три равны, они оба указывают на один и тот же файл:
Как мне сравнить 2 строки, чтобы определить, ссылаются ли они на один и тот же путь в Win32 с помощью C/С++?
В то время как это будет обрабатывать множество случаев, оно пропускает некоторые вещи:
_tcsicmp(szPath1, szPath2) == 0
Например:
косые черты/обратные косые черты
относительные/абсолютные пути.
[Изменить] Название изменено в соответствии с существующим вопросом С#.
Откройте оба файла с помощью CreateFile
, вызовите GetFileInformationByHandle
для обоих и сравните dwVolumeSerialNumber
, nFileIndexLow
, nFileIndexHigh
. Если все три равны, они оба указывают на один и тот же файл:
используйте GetFullPathName из kernel32.dll, это даст вам абсолютный путь к файлу. Затем сравните его с другим путем, который вы используете с помощью простого сравнения строк.
edit: code
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);
приведенный выше код будет печатать один и тот же путь для всех трех представлений пути. После этого вы можете сделать поиск без учета регистра
Посмотрите на этот вопрос: Лучший способ определить, есть ли ссылка на два пути к одному файлу в С#
Вопрос о С#, но ответ - это просто вызов API Win32 GetFileInformationByHandle
.
Простое сравнение строк недостаточно для сравнения путей для равенства. В окнах вполне возможно c:\foo\bar.txt и c:\temp\bar.txt указать точно такой же файл через символические и жесткие ссылки в файловой системе.
Сравнение путей, по сути, существенно заставляет вас открывать оба файла и сравнивать информацию о нижнем уровне. Любой другой метод будет иметь шелушащиеся результаты.
Посмотрите на эту отличную статью, которую сделал Люциан по этому вопросу. Код находится в VB, но он довольно переводится на C/С++, поскольку он PInvoke'd использует большинство методов.
http://blogs.msdn.com/vbteam/archive/2008/09/22/to-compare-two-filenames-lucian-wischik.aspx
Если у вас есть доступ к библиотекам 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
разрешены для одного и того же объекта файловой системы, else false
.
Основываясь на ответах 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;
}
Что вам нужно сделать, так это получить канонический путь.
Для каждого пути вы попросите файловую систему преобразовать в канонический путь или предоставить вам идентификатор, который однозначно идентифицирует файл (например, iNode).
Затем сравните канонический путь или уникальный идентификатор.
Примечание: Не пытайтесь выяснить, как коническая дорожка сама файловая система может делать что-то с символическими ссылками и т.д., Которые нелегко обрабатываются, если вы не знакомы с файловой системой.
Сравнение фактических строк пути не даст точных результатов, если вы ссылаетесь на UNC или канонические пути (то есть на все, кроме локального пути).
shlwapi.h имеет несколько Функции пути, которые могут вам пригодиться в определении того, являются ли ваши пути одинаковыми.
Он содержит функции, такие как PathIsRoot, которые могут использоваться в функции большей области.
Откройте оба файла и используйте GetFinalPathNameByHandle()
против HANDLE
s. Затем сравните пути.
Если файлы существуют, и вы можете иметь дело с потенциальным состоянием гонки, и производительность удалась от открытия файлов, то несовершенное решение, которое должно работать на любой платформе, - это открыть один файл для записи самостоятельно, закрыть его, а затем открыть для записи снова после открытия другого файла для записи. Поскольку доступ на запись разрешен только для исключительных ситуаций, если вы могли открыть первый файл для записи в первый раз, но не во второй раз, скорее всего, вы заблокировали свой собственный запрос при попытке открыть оба файла.
(возможно, конечно, также, что в какой-то другой части системы открыт один из ваших файлов)