Лучший способ определить, если две ссылки на один и тот же файл в Windows? - PullRequest
16 голосов
/ 18 февраля 2009

Как бы я сравнил 2 строки, чтобы определить, ссылаются ли они на один и тот же путь в Win32 с использованием C / C ++?

Хотя это будет обрабатывать множество случаев, в нем пропущены некоторые вещи:

_tcsicmp(szPath1, szPath2) == 0

Например:

  • косая черта / обратная косая черта

  • относительные / абсолютные пути.

[Изменить] Заголовок изменен в соответствии с существующим вопросом C #.

Ответы [ 11 ]

33 голосов
/ 18 февраля 2009

Откройте оба файла с помощью CreateFile, вызовите GetFileInformationByHandle для обоих и сравните dwVolumeSerialNumber, nFileIndexLow, nFileIndexHigh. Если все три равны, они оба указывают на один и тот же файл:

GetFileInformationByHandle функция

BY_HANDLE_FILE_INFORMATION Структура

5 голосов
/ 18 февраля 2009

См. Этот вопрос: Лучший способ определить, есть ли две пути ссылки на один и тот же файл в C #

Вопрос касается C #, но ответом является только вызов Win32 API GetFileInformationByHandle.

5 голосов
/ 18 февраля 2009

используйте GetFullPathName из kernel32.dll, это даст вам абсолютный путь к файлу. Затем сравните его с другим путем, который вы используете, используя простую строку сравнения

редактировать: код

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 голосов
/ 13 мая 2018

Библиотека файловой системы

Начиная с C ++ 17 вы можете использовать стандартную библиотеку файловой системы . Включите его, используя #include <filesystem>. Вы можете получить к нему доступ даже в старых версиях C ++, см. Сноску.

Функция, которую вы ищете: equivalent, под пространством имен std::filesystem:

bool std::filesystem::equivalent(const std::filesystem::path& p1, const filesystem::path& p2 );

Подводя итог документации : эта функция принимает два пути в качестве параметров и возвращает 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. Смотри документацию здесь .
Например:

#include <experimental/filesystem>
//...
std::experimental::filesystem::equivalent(p1, p2);
3 голосов
/ 21 мая 2013

Если у вас есть доступ к библиотекам 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 голосов
/ 18 февраля 2009

Простого сравнения строк недостаточно для сравнения путей на равенство. В Windows вполне возможно, что c: \ foo \ bar.txt и c: \ temp \ bar.txt указывают на один и тот же файл через символические и жесткие ссылки в файловой системе.

Правильное сравнение путей по существу вынуждает вас открыть оба файла и сравнить информацию о дескрипторе низкого уровня. Любой другой метод будет иметь плохие результаты.

Посмотри на этот замечательный пост, сделанный Люцианом на эту тему. Код написан на VB, но его можно перевести на C / C ++, так как он использовал большинство методов PInvoke.

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

2 голосов
/ 19 февраля 2009

Что вам нужно сделать, это получить канонический путь.

Для каждого пути вы просите файловую систему преобразовать в канонический путь или дать вам идентификатор, который однозначно идентифицирует файл (например, iNode).

Затем сравните канонический путь или уникальный идентификатор.

Примечание: Не пытайтесь самостоятельно определить конический путь, который файловая система может выполнять с символическими ссылками и т. Д., Которые трудно отследить, если вы не очень хорошо знакомы с файловой системой.

2 голосов
/ 19 февраля 2009

На основе ответов о 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 голосов
/ 16 марта 2014

Откройте оба файла и используйте GetFinalPathNameByHandle() против HANDLE s. Затем сравните пути.

0 голосов
/ 04 января 2010

Если файлы существуют, и вы можете справиться с потенциальным состоянием гонки и ухудшением производительности при открытии файлов, несовершенное решение, которое должно работать на любой платформе, - это открыть один файл для записи самостоятельно, закрыть его, а затем открыть для записи снова после открытия другого файла для записи. Поскольку доступ на запись должен быть разрешен только как исключительный, если вы смогли открыть первый файл для записи в первый раз, но не во второй раз, то есть вероятность, что вы заблокировали свой собственный запрос при попытке открыть оба файла.

(есть вероятность, что в какой-то другой части системы открыт один из ваших файлов)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...