Как я могу сравнить (каталог) пути в C#?
если у меня есть два DirectoryInfo
объекты, как я могу сравнить их семантического равенства? Например, все следующие пути должны считаться равными C:temp
:
C:temp
C:temp
C:temp.
C:tempx....temp.
следующее Может быть или не быть равно C:temp
:
-
temp
если текущий рабочий каталог находится на дискеC:
-
temp
если текущий рабочий каталогC:
C:temp.
C:temp...
если важно рассмотреть текущий рабочий каталог, я могу понять это сам, так что это не так важно. Конечные точки разделены в windows, поэтому эти пути действительно должны быть равны - но они не разделены в unix, поэтому в mono я ожидал бы других результатов.
чувствительность к регистру не является обязательным. Пути могут существовать или не существовать, и пользователь может или может не иметь разрешений на путь - я бы предпочел быстрый надежный метод, который не требует ввода-вывода (поэтому нет проверки разрешений), но если есть что-то встроенное, я был бы доволен чем-то "достаточно хорошим"...
12 ответов:
С ответ этот метод может использоваться в нескольких случаях:
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);
должен работать как для файлов, так и для каталогов.
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)
есть некоторые короткие приходит к реализации путей в. NET. there есть много жалоб на это. Патрик Smacchia создатель вопросом, что происходит, опубликовал библиотека с открытым исходным кодом, которая позволяет обрабатывать общие и сложные операции пути. Если вы выполняете много операций сравнения по путям в своем приложении, эта библиотека может быть вам полезна.
Я понимаю, что это старый пост, но все ответы в конечном итоге основаны на текстовом сравнении двух имен. Попытка получить два "нормализованных" имени, которые учитывают множество возможных способов ссылки на один и тот же файловый объект, почти невозможна. Существуют такие проблемы, как: соединения, символические ссылки, сетевые файловые ресурсы (ссылки на один и тот же файловый объект разными способами) и т. д. так далее. в самом деле, каждый ответ выше, за исключением Игоря Корхова, абсолютно даст неверные результаты при определенных обстоятельствах (например, соединения, символьные ссылки, ссылки на каталоги, и т. д.)
вопрос специально просил, чтобы решение не требовало ввода-вывода, но если вы собираетесь иметь дело с сетевыми путями, вам абсолютно нужно будет сделать IO: есть случаи, когда просто невозможно определить из любой локальной манипуляции со строкой пути, будут ли две ссылки на файл ссылаться на один и тот же физический файл. (Это может легко понять следующее. Предположим, что файловый сервер имеет соединение каталогов windows где-то в пределах общего поддерева. В этом случае на файл можно ссылаться либо напрямую, либо через соединение. Но соединение находится на файловом сервере, и поэтому клиент просто не может определить, чисто через локальную информацию, что два ссылающихся имени файла относятся к одному и тому же физическому файлу: информация просто не доступна локально клиенту. Таким образом, нужно абсолютно сделайте некоторый минимальный IO-например, откройте два дескриптора объекта file - чтобы определить, относятся ли ссылки к одному и тому же физическому файлу.)
следующее решение делает некоторый IO, хотя и очень минимальный, но правильно определяет, являются ли две ссылки на файловую систему семантически идентичными, т. е. ссылаются на один и тот же файловый объект. (если ни одна из спецификаций файла не относится к допустимому файловому объекту, все ставки отключены):
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; }
идея для этого пришла из ответа Уоррена Стивенса в аналогичный вопрос я отправил на суперпользователя:https://superuser.com/a/881966/241981
кажется, что P / Invoking GetFinalPathNameByHandle() было бы самым надежным решением.
UPD: Ой, я не учел ваше желание не использовать ни одного ввода / вывода
Microsoft реализовала аналогичные методы, хотя они не так полезны, как ответы выше:
- PathUtil.Метод ArePathsEqual (что просто
return string.Equals(path1, path2, StringComparison.OrdinalIgnoreCase);
)- PathUtil.Нормализовать Метод
- PathUtil.Способ NormalizePath (что просто
return PathUtil.Normalize(path);
)
свойства "имя" равны. Возьмите:
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, которые отличаются.вы можете сделать рекурсивный метод для изучения свойств имен каждого родителя, учитывая два класса DirectoryInfo, чтобы гарантировать, что полный путь будет одинаковым.
EDIT: это работает для вашей ситуации? Создайте консольное приложение и вставьте его поверх вся программа.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; } } }
вы можете использовать Minimatch, порт узла.Яш' minimatch.
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);
понять, почему the необходимо:на пути в стиле Windows Синтаксис Minimatch был разработан для путей в стиле Linux (только с косыми чертами). В частности, он использует обратную косую черту в качестве escape-символа, поэтому он не может просто принимать пути в стиле Windows. Моя версия C# сохраняет это поведение.
чтобы подавить это, и позвольте как обратные, так и прямые косые черты в качестве разделителей пути (в шаблонах или входных данных) установите :
var mm = new Minimatcher(searchPattern, new Options { AllowWindowsPaths = true });
передача этой опции полностью отключит escape-символы.
using System; using System.Collections.Generic; using System.Text; namespace EventAnalysis.IComparerImplementation { public sealed class FSChangeElemComparerByPath : IComparer<FSChangeElem> { public int Compare(FSChangeElem firstPath, FSChangeElem secondPath) { return firstPath.strObjectPath == null ? (secondPath.strObjectPath == null ? 0 : -1) : (secondPath.strObjectPath == null ? 1 : ComparerWrap(firstPath.strObjectPath, secondPath.strObjectPath)); } private int ComparerWrap(string stringA, string stringB) { int length = 0; int start = 0; List<string> valueA = new List<string>(); List<string> valueB = new List<string>(); ListInit(ref valueA, stringA); ListInit(ref valueB, stringB); if (valueA.Count != valueB.Count) { length = (valueA.Count > valueB.Count) ? valueA.Count : valueB.Count; if (valueA.Count != length) { for (int i = 0; i < length - valueA.Count; i++) { valueA.Add(string.Empty); } } else { for (int i = 0; i < length - valueB.Count; i++) { valueB.Add(string.Empty); } } } else length = valueA.Count; return RecursiveComparing(valueA, valueB, length, start); } private void ListInit(ref List<string> stringCollection, string stringToList) { foreach (string s in stringToList.Remove(0, 2).Split('\')) { stringCollection.Add(s); } } private int RecursiveComparing(List<string> valueA, List<string> valueB, int length, int start) { int result = 0; if (start != length) { if (valueA[start] == valueB[start]) { result = RecursiveComparing(valueA, valueB, length, ++start); } else { result = String.Compare(valueA[start], valueB[start]); } } else return 0; return result; } } }