Wenn ich zwei DirectoryInfo
Objekte habe, wie kann ich sie auf semantische Gleichheit vergleichen? Beispielsweise sollten die folgenden Pfade alle als gleich angesehen werden C:\temp
:
C:\temp
C:\temp\
C:\temp\.
C:\temp\x\..\..\temp\.
Folgendes kann gleich sein oder nicht C:\temp
:
\temp
Wenn sich das aktuelle Arbeitsverzeichnis auf dem Laufwerk befindetC:\
temp
wenn das aktuelle Arbeitsverzeichnis istC:\
C:\temp.
C:\temp...\
Wenn es wichtig ist, das aktuelle Arbeitsverzeichnis zu berücksichtigen, kann ich das selbst herausfinden, das ist also nicht so wichtig. Nachgestellte Punkte werden in Fenstern entfernt, daher sollten diese Pfade wirklich gleich sein - aber sie werden unter Unix nicht entfernt, daher würde ich unter Mono andere Ergebnisse erwarten.
Die Groß- und Kleinschreibung ist optional. Die Pfade können vorhanden sein oder nicht, und der Benutzer kann Berechtigungen für den Pfad haben oder nicht - ich würde eine schnelle, robuste Methode bevorzugen, die keine E / A erfordert (also keine Berechtigungsprüfung), aber wenn etwas erstellt wurde -in Ich würde mich auch über alles freuen, was "gut genug" ist ...
quelle
System.IO.DirectoryInfo
implementiert die Klasse dies nichtbool Equals(DirectoryInfo other)
? Mir scheint, dass dieses Zeug inzwischen so standardisiert sein sollte, dass wir nicht einmal in der Lage sein sollten , einfache Dinge wie den Vergleich zweier Pfade durcheinander zu bringen.Antworten:
Ausgehend von dieser Antwort kann diese Methode einige Randfälle behandeln:
public static string NormalizePath(string path) { return Path.GetFullPath(new Uri(path).LocalPath) .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) .ToUpperInvariant(); }
Weitere Details in der Originalantwort. Nennen Sie es wie:
bool pathsEqual = NormalizePath(path1) == NormalizePath(path2);
Sollte sowohl für Datei- als auch für Verzeichnispfade funktionieren.
quelle
new Uri(path).LocalPath
- würde im Falle eines#
Symbols im Pfad einen falschen PfadToUpperInvariant
in einem Dateisystempfad verwendet? Herzliche Glückwünsche. Sie haben jetzt eine Anwendung, die auf Betriebssystemen mit türkischen regionalen Einstellungen auf mysteriöse Weise in die Luft jagen kann.GetFullPath
scheint die Arbeit zu erledigen, mit Ausnahme von case different (Path.GetFullPath("test") != Path.GetFullPath("TEST")
) und Trailing Slash. Der folgende Code sollte also gut funktionieren:String.Compare( Path.GetFullPath(path1).TrimEnd('\\'), Path.GetFullPath(path2).TrimEnd('\\'), StringComparison.InvariantCultureIgnoreCase)
Oder wenn Sie beginnen möchten mit
DirectoryInfo
:String.Compare( dirinfo1.FullName.TrimEnd('\\'), dirinfo2.FullName.TrimEnd('\\'), StringComparison.InvariantCultureIgnoreCase)
quelle
Es gibt einige kurze Hinweise zur Implementierung von Pfaden in .NET. Es gibt viele Beschwerden darüber. Patrick Smacchia , der Erfinder von NDepend, veröffentlichte eine Open-Source-Bibliothek, die die Handhabung allgemeiner und komplexer Pfadoperationen ermöglicht . Wenn Sie viele Vergleichsvorgänge für Pfade in Ihrer Anwendung ausführen, kann diese Bibliothek für Sie hilfreich sein.
quelle
Mir ist klar, dass dies ein alter Beitrag ist, aber alle Antworten basieren letztendlich auf einem Textvergleich zweier Namen. Der Versuch, zwei "normalisierte" Namen zu erhalten, die die Vielzahl möglicher Verweismöglichkeiten auf dasselbe Dateiobjekt berücksichtigen, ist nahezu unmöglich. Es gibt Probleme wie: Junctions, symbolische Links, Netzwerkdateifreigaben (die auf unterschiedliche Weise auf dasselbe Dateiobjekt verweisen ) usw. usw. Tatsächlich führt jede einzelne Antwort oben, mit Ausnahme von Igor Korkhovs, absolut zu falschen Ergebnissen bestimmte Umstände (z. B. Kreuzungen, symbolische Links, Verzeichnislinks usw.)
In der Frage wurde ausdrücklich gefordert, dass für die Lösung keine E / A erforderlich sind. Wenn Sie sich jedoch mit vernetzten Pfaden befassen möchten, müssen Sie unbedingt E / A ausführen: Es gibt Fälle, in denen es einfach nicht möglich ist, anhand einer lokalen Pfadzeichenfolge zu bestimmen Manipulation, ob zwei Dateiverweise auf dieselbe physische Datei verweisen. (Dies kann wie folgt leicht verstanden werden. Angenommen, ein Dateiserver verfügt über eine Windows-Verzeichnisverbindung an einer Stelle innerhalb eines gemeinsam genutzten Teilbaums. In diesem Fall kann auf eine Datei entweder direkt oder über die Verbindung verwiesen werden. Die Verbindung befindet sich jedoch auf dem Dateiserver. Daher ist es für einen Client einfach unmöglich, allein durch lokale Informationen festzustellen, dass sich die beiden referenzierenden Dateinamen auf dieselbe physische Datei beziehen: Die Informationen stehen dem Client einfach nicht lokal zur Verfügung.
Die folgende Lösung führt einige E / A-Vorgänge aus, obwohl sie sehr minimal sind, bestimmt jedoch korrekt, ob zwei Dateisystemreferenzen semantisch identisch sind, dh auf dasselbe Dateiobjekt verweisen. (Wenn sich keine der Dateispezifikationen auf ein gültiges Dateiobjekt bezieht, sind alle Wetten deaktiviert):
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; }
Die Idee dazu kam von einer Antwort von Warren Stevens in einer ähnlichen Frage, die ich auf SuperUser gepostet habe: https://superuser.com/a/881966/241981
quelle
Es scheint, dass P / Invoking GetFinalPathNameByHandle () die zuverlässigste Lösung wäre.
UPD: Ups, ich habe Ihren Wunsch, keine E / A zu verwenden, nicht berücksichtigt
quelle
GetFullPath()
nicht. Es liegt also an Ihnen, zu entscheiden, ob Sie echte semantische Gleichheit benötigen oder nicht.quelle
Microsoft hat ähnliche Methoden implementiert, obwohl diese nicht so nützlich sind wie die obigen Antworten:
return string.Equals(path1, path2, StringComparison.OrdinalIgnoreCase);
)return PathUtil.Normalize(path);
)quelle
Die Eigenschaften "Name" sind gleich. Nehmen:
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
("Scratch" in diesem Fall. Dir3 == "4760".) Nur die FullName-Eigenschaften unterscheiden sich.Möglicherweise können Sie eine rekursive Methode ausführen, um die Namenseigenschaften jedes übergeordneten Elements anhand Ihrer beiden DirectoryInfo-Klassen zu überprüfen und sicherzustellen, dass der vollständige Pfad identisch ist.
EDIT : Funktioniert das für Ihre Situation? Erstellen Sie eine Konsolenanwendung und fügen Sie diese über die gesamte Program.cs-Datei ein. Wenn Sie der Funktion AreEquals () zwei DirectoryInfo-Objekte bereitstellen, wird True zurückgegeben, wenn sie dasselbe Verzeichnis sind.
AreEquals()
Wenn Sie möchten, können Sie diese Methode möglicherweise als Erweiterungsmethode für DirectoryInfo optimieren , sodass Sie dies einfach tun könnenmyDirectoryInfo.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; } } }
quelle
D:\temp
vs.C:\temp
. Schöne Idee; Sie haben sich jedoch nicht mit Groß- und Kleinschreibung befasst, und es könnte etwas kürzer sein: while (dir1! = null && dir2! = null) if (! string.Equals (dir1.Name, dir2.Name, StringComparison.InvariantCultureIgnoreCase)) falsch zurückgeben; sonst {dir1 = dir1.Parent; dir2 = dir2.Parent;} return dir1 == dir2;Sie können Minimatch verwenden, einen Port des Minimatch von 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);
Sehen Sie, warum die
AllowWindowsPaths = true
Option erforderlich ist:Nuget: http://www.nuget.org/packages/Minimatch/
GitHub: https://github.com/SLaks/Minimatch
quelle
bool equals = myDirectoryInfo1.FullName == myDirectoryInfo2.FullName;
?
quelle
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; } } }
quelle
bool Equals(string path1, string path2) { return new Uri(path1) == new Uri(path2); }
Der Uri-Konstruktor normalisiert den Pfad.
quelle
Ich habe Rekursion verwendet, um dieses Problem für mich selbst zu lösen.
public bool PathEquals(string Path1, string Path2) { FileInfo f1 = new FileInfo(Path1.Trim('\\','/','.')); FileInfo f2 = new FileInfo(Path2.Trim('\\', '/','.')); if(f1.Name.ToLower() == f2.Name.ToLower()) { return DirectoryEquals(f1.Directory, f2.Directory); } else { return false; } } public bool DirectoryEquals(DirectoryInfo d1, DirectoryInfo d2) { if(d1.Name.ToLower() == d2.Name.ToLower()) { if((d1.Parent != null) && (d2.Parent != null)) { return DirectoryEquals(d1.Parent, d2.Parent); } else { return true;//C:\Temp1\Temp2 equals \Temp1\Temp2 //return (d1.Parent == null) && (d2.Parent == null);//C:\Temp1\Temp2 does not equal \Temp1\Temp2 } } else { return false; } }
Hinweis:
new FileInfo(path)
Gibt eine gültige FileInfo zurück, auch wenn der Pfad keine Datei ist (das Namensfeld entspricht dem Verzeichnisnamen).quelle