Как я могу сравнить (каталог) пути в C #? - PullRequest
69 голосов
/ 17 февраля 2010

Если у меня есть два DirectoryInfo объекта, как я могу сравнить их для семантического равенства? Например, следующие пути должны считаться равными C:\temp:

  • C:\temp
  • C:\temp\
  • C:\temp\.
  • C:\temp\x\..\..\temp\.

Следующее может или не может быть равным C:\temp:

  • \temp, если текущий рабочий каталог находится на диске C:\
  • temp, если текущий рабочий каталог C:\
  • C:\temp.
  • C:\temp...\

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

Чувствительность к регистру не обязательна. Пути могут или не могут существовать, и пользователь может иметь или не иметь разрешения на путь - я бы предпочел быстрый надежный метод, который не требует ввода-вывода (так что проверка прав не требуется), но если что-то построено -Я бы тоже был доволен чем-нибудь "достаточно хорошим" ...

Ответы [ 12 ]

91 голосов
/ 17 февраля 2010

GetFullPath, кажется, делает работу, за исключением разницы в регистре (Path.GetFullPath("test") != Path.GetFullPath("TEST")) и косой черты. Итак, следующий код должен работать нормально:

String.Compare(
    Path.GetFullPath(path1).TrimEnd('\\'),
    Path.GetFullPath(path2).TrimEnd('\\'), 
    StringComparison.InvariantCultureIgnoreCase)

Или, если вы хотите начать с DirectoryInfo:

String.Compare(
    dirinfo1.FullName.TrimEnd('\\'),
    dirinfo2.FullName.TrimEnd('\\'), 
    StringComparison.InvariantCultureIgnoreCase)
31 голосов
/ 11 января 2014

С этот ответ , этот метод может обрабатывать несколько крайних случаев:

public static string NormalizePath(string path)
{
    return Path.GetFullPath(new Uri(path).LocalPath)
               .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
               .ToUpperInvariant();
}

Подробнее в оригинальном ответе. Назовите это как:

bool pathsEqual = NormalizePath(path1) == NormalizePath(path2);

Должно работать как для файлов, так и для путей к каталогам.

11 голосов
/ 17 февраля 2010

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

7 голосов
/ 08 сентября 2016

Я понимаю, что это старый пост, но все ответы в конечном итоге основаны на текстовом сравнении двух имен. Попытка получить два «нормализованных» имени, которые учитывают множество возможных способов ссылки на один и тот же файловый объект, практически невозможна. Существуют такие проблемы, как: переходы, символические ссылки, общие сетевые файлы (ссылки на один и тот же файловый объект различными способами) и т. Д. Фактически, каждый отдельный ответ, за исключением Игоря Корхова, даст абсолютно неверные результаты при определенных обстоятельствах (например, соединения, символические ссылки, ссылки в каталогах и т. д.)

В вопросе конкретно просили, чтобы решение не требовало ввода-вывода, но если вы собираетесь работать с сетевыми путями, вам абсолютно необходимо будет выполнить ввод-вывод: есть случаи, когда просто невозможно определить из какого-либо локального манипулирование строкой пути, будут ли две ссылки на файл ссылаться на один и тот же физический файл. (Это легко понять следующим образом. Предположим, что на файловом сервере есть место соединения с каталогом Windows где-то в пределах общего поддерева. В этом случае на файл можно ссылаться либо напрямую, либо через соединение. Но соединение находится на файловом сервере, и поэтому для клиента просто невозможно определить, только с помощью локальной информации, что два именующих файла ссылаются на один и тот же физический файл: информация просто недоступна локально для клиента. Таким образом, абсолютно необходимо выполнить минимальный ввод-вывод - например, откройте два дескриптора объекта файла - чтобы определить, ссылаются ли ссылки на один и тот же физический файл.)

Следующее решение выполняет некоторый ввод-вывод, хотя и очень минимальный, но правильно определяет, являются ли две ссылки на файловую систему семантически идентичными, то есть ссылаются на один и тот же объект файла. (если ни одна из спецификаций файла не ссылается на действительный объект файла, все ставки отключены):

    public static bool AreFileSystemObjectsEqual(string dirName1, string dirName2)
    {
        //Optimization: if strings are equal, don't bother with the IO
        bool bRet = string.Equals(dirName1, dirName2, StringComparison.OrdinalIgnoreCase);
        if (!bRet)
        {
            //NOTE: we cannot lift the call to GetFileHandle out of this routine, because we _must_
            // have both file handles open simultaneously in order for the objectFileInfo comparison
            // to be guaranteed as valid.
            using (SafeFileHandle directoryHandle1 = GetFileHandle(dirName1), directoryHandle2 = GetFileHandle(dirName2))
            {
                BY_HANDLE_FILE_INFORMATION? objectFileInfo1 = GetFileInfo(directoryHandle1);
                BY_HANDLE_FILE_INFORMATION? objectFileInfo2 = GetFileInfo(directoryHandle2);
                bRet = objectFileInfo1 != null
                       && objectFileInfo2 != null
                       && (objectFileInfo1.Value.FileIndexHigh == objectFileInfo2.Value.FileIndexHigh)
                       && (objectFileInfo1.Value.FileIndexLow == objectFileInfo2.Value.FileIndexLow)
                       && (objectFileInfo1.Value.VolumeSerialNumber == objectFileInfo2.Value.VolumeSerialNumber);
            }
        }
        return bRet;
    }

Идея для этого возникла из ответа Уоррена Стивенса на аналогичный вопрос, который я разместил в SuperUser: https://superuser.com/a/881966/241981

3 голосов
/ 17 февраля 2010

Похоже, что P / Invoking GetFinalPathNameByHandle () будет самым надежным решением.

UPD: Ой, я не учел ваше желание не использовать ввод / вывод

3 голосов
/ 17 февраля 2010
 System.IO.Path.GetFullPath(pathA).Equals(System.IO.Path.GetFullPath(PathB));
2 голосов
/ 02 декабря 2015

Microsoft реализовала подобные методы, хотя они не так полезны, как ответы выше:

1 голос
/ 26 июня 2014

Вы можете использовать Minimatch, порт minimatch Node.js.

var mm = new Minimatcher(searchPattern, new Options { AllowWindowsPaths = true });

if (mm.IsMatch(somePath))
{
    // The path matches!  Do some cool stuff!
}

var matchingPaths = mm.Filter(allPaths);


Узнайте, почему необходима опция AllowWindowsPaths = true:

На пути в стиле Windows Синтаксис Minimatch был разработан для путей в стиле Linux (только с косой чертой). В частности, он использует обратную косую черту как escape-символ, поэтому он не может просто принимать пути в стиле Windows. Моя версия C # сохраняет это поведение.

Чтобы подавить это и разрешить как обратную, так и прямую наклонную черту в качестве разделителей пути (в шаблонах или входных данных), установите параметр AllowWindowsPaths:

var mm = new Minimatcher(searchPattern, new Options { AllowWindowsPaths = true });

Передача этой опции полностью отключит экранирующие символы.

Nuget: http://www.nuget.org/packages/Minimatch/

GitHub: https://github.com/SLaks/Minimatch

1 голос
/ 17 февраля 2010

Свойства "Имя" равны. Возьмите: * +1001 *

DirectoryInfo dir1 = new DirectoryInfo("C:\\Scratch");
DirectoryInfo dir2 = new DirectoryInfo("C:\\Scratch\\");
DirectoryInfo dir3 = new DirectoryInfo("C:\\Scratch\\4760");
DirectoryInfo dir4 = new DirectoryInfo("C:\\Scratch\\4760\\..\\");

dir1.Name == dir2.Name and dir2.Name == dir4.Name (в данном случае "Скретч". Dir3 == "4760".) Отличаются только свойства FullName.

Возможно, вы сможете сделать рекурсивный метод для проверки свойств Name каждого родителя с учетом ваших двух классов DirectoryInfo, чтобы убедиться, что полный путь совпадает.

РЕДАКТИРОВАТЬ : это работает для вашей ситуации? Создайте консольное приложение и вставьте его поверх всего файла Program.cs. Укажите два объекта DirectoryInfo для функции AreEquals (), и она вернет True, если они находятся в одном каталоге. Возможно, вы сможете настроить этот AreEquals() метод для расширения в DirectoryInfo, если хотите, так что вы можете просто сделать myDirectoryInfo.IsEquals(myOtherDirectoryInfo);

using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(AreEqual(
                new DirectoryInfo("C:\\Scratch"),
                new DirectoryInfo("C:\\Scratch\\")));

            Console.WriteLine(AreEqual(
                new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework"),
                new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework\\v3.5\\1033\\..\\..")));

            Console.WriteLine(AreEqual(
                new DirectoryInfo("C:\\Scratch\\"),
                new DirectoryInfo("C:\\Scratch\\4760\\..\\..")));

            Console.WriteLine("Press ENTER to continue");
            Console.ReadLine();
        }

        private static bool AreEqual(DirectoryInfo dir1, DirectoryInfo dir2)
        {
            DirectoryInfo parent1 = dir1;
            DirectoryInfo parent2 = dir2;

            /* Build a list of parents */
            List<string> folder1Parents = new List<string>();
            List<string> folder2Parents = new List<string>();

            while (parent1 != null)
            {
                folder1Parents.Add(parent1.Name);
                parent1 = parent1.Parent;
            }

            while (parent2 != null)
            {
                folder2Parents.Add(parent2.Name);
                parent2 = parent2.Parent;
            }

            /* Now compare the lists */

            if (folder1Parents.Count != folder2Parents.Count)
            {
                // Cannot be the same - different number of parents
                return false;
            }

            bool equal = true;

            for (int i = 0; i < folder1Parents.Count && i < folder2Parents.Count; i++)
            {
                equal &= folder1Parents[i] == folder2Parents[i];
            }

            return equal;
        }
    }
}
0 голосов
/ 05 апреля 2014
bool Equals(string path1, string path2)
{
    return new Uri(path1) == new Uri(path2);
}

Конструктор Uri нормализует путь.

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