Comment puis-je comparer les chemins (répertoire) en C #?

Si j’ai deux objects DirectoryInfo , comment puis-je les comparer pour l’égalité sémantique? Par exemple, les chemins suivants doivent tous être considérés comme égaux à C:\temp :

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

Ce qui suit peut être ou ne pas être égal à C:\temp :

  • \temp si le répertoire de travail en cours est sur le lecteur C:\
  • temp si le répertoire de travail actuel est C:\
  • C:\temp.
  • C:\temp...\

S’il est important de prendre en compte le répertoire de travail actuel, je peux le comprendre moi-même, ce n’est pas si important. Les points de fin sont dépouillés dans les fenêtres, donc ces chemins devraient vraiment être égaux – mais ils ne sont pas dépouillés dans unix, donc sous mono j’attendrais d’autres résultats.

La sensibilité à la casse est facultative. Les chemins peuvent exister ou ne pas exister, et l’utilisateur peut ou non avoir des permissions sur le chemin – je préférerais une méthode rapide et robuste qui ne nécessite pas d’E / S (donc pas de vérification des permissions), mais s’il y a quelque chose de construit Je serais content de quelque chose de “bien” aussi …

De cette réponse , cette méthode peut gérer quelques cas extrêmes:

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

Plus de détails dans la réponse originale. Appelez ça comme:

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

Devrait fonctionner pour les chemins de fichier et de répertoire.

GetFullPath semble faire le travail, à l’exception de la différence de casse ( Path.GetFullPath("test") != Path.GetFullPath("TEST") ) et de la barre oblique Path.GetFullPath("test") != Path.GetFullPath("TEST") . Donc, le code suivant devrait fonctionner correctement:

 Ssortingng.Compare( Path.GetFullPath(path1).TrimEnd('\\'), Path.GetFullPath(path2).TrimEnd('\\'), SsortingngComparison.InvariantCultureIgnoreCase) 

Ou, si vous voulez commencer avec DirectoryInfo :

 Ssortingng.Compare( dirinfo1.FullName.TrimEnd('\\'), dirinfo2.FullName.TrimEnd('\\'), SsortingngComparison.InvariantCultureIgnoreCase) 

La mise en œuvre des chemins dans .NET présente des lacunes. Il y a beaucoup de plaintes à ce sujet. Pasortingck Smacchia , le créateur de NDepend, a publié une bibliothèque open source qui permet de gérer des opérations de chemins communs et complexes . Si vous faites beaucoup d’opérations de comparaison sur les chemins de votre application, cette bibliothèque pourrait vous être utile.

Je me rends compte que c’est un ancien message, mais toutes les réponses sont finalement basées sur une comparaison textuelle de deux noms. Essayer d’obtenir deux noms “normalisés”, qui prennent en compte la myriade de manières possibles de référencer le même object fichier, est presque impossible. Il y a des problèmes tels que: jonctions, liens symboliques, partages de fichiers réseau (référençant le même object fichier de différentes manières), etc. En fait, chaque réponse ci-dessus, à l’exception de Igor Korkhov, donnera des résultats incorrects. certaines circonstances (par exemple, jonctions, liens symboliques, liens de répertoire, etc.)

La question demandait spécifiquement que la solution ne nécessite aucune E / S, mais si vous envisagez de gérer des chemins en réseau, vous devrez absolument faire des E / S: il est parfois impossible de déterminer à partir d’une chaîne de chemin locale manipulation, si deux références de fichier référenceront le même fichier physique. (Cela peut être facilement compris comme suit. Supposons qu’un serveur de fichiers possède une jonction de répertoires Windows quelque part dans un sous-arbre partagé. Dans ce cas, un fichier peut être référencé directement ou via la jonction. Il est donc impossible pour un client de déterminer, par le biais d’informations locales uniquement, que les deux noms de fichiers référencés se réfèrent au même fichier physique: les informations ne sont simplement pas disponibles localement pour le client. par exemple, ouvrir deux descripteurs d’object de fichier – pour déterminer si les références font référence au même fichier physique.)

La solution suivante fait des entrées-sorties, bien que très minimes, mais détermine correctement si deux références de système de fichiers sont sémantiquement identiques, c’est-à-dire référencent le même object fichier. (Si aucune spécification de fichier ne fait référence à un object de fichier valide, tous les paris sont désactivés):

  public static bool AreFileSystemObjectsEqual(ssortingng dirName1, ssortingng dirName2) { //Optimization: if ssortingngs are equal, don't bother with the IO bool bRet = ssortingng.Equals(dirName1, dirName2, SsortingngComparison.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; } 

L’idée de cela est venue d’une réponse de Warren Stevens dans une question similaire que j’ai postée sur SuperUser: https://superuser.com/a/881966/241981

  System.IO.Path.GetFullPath(pathA).Equals(System.IO.Path.GetFullPath(PathB)); 

Il semble que P / Invoquer GetFinalPathNameByHandle () soit la solution la plus fiable.

UPD: Oups, je n’ai pas pris en compte votre désir de ne pas utiliser d’E / S

Microsoft a mis en œuvre des méthodes similaires, même si elles ne sont pas aussi utiles que les réponses ci-dessus:

  • Méthode PathUtil.ArePathsEqual (qui return ssortingng.Equals(path1, path2, SsortingngComparison.OrdinalIgnoreCase); simplement return ssortingng.Equals(path1, path2, SsortingngComparison.OrdinalIgnoreCase); )
  • Méthode PathUtil.Normalize
  • PathUtil.NormalizePath, méthode (qui return PathUtil.Normalize(path); simplement return PathUtil.Normalize(path); )

Les propriétés “Nom” sont égales. Prendre:

 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” dans ce cas. dir3 == “4760”). Seules les propriétés FullName sont différentes.

Vous pouvez faire une méthode récursive pour examiner les propriétés Name de chaque parent en fonction de vos deux classes DirectoryInfo pour vous assurer que le chemin d’access complet est le même.

EDIT : cela fonctionne-t-il pour votre situation? Créez une application console et collez-la sur tout le fichier Program.cs. Fournissez deux objects DirectoryInfo à la fonction AreEquals () et elle retournera True si le même répertoire est utilisé. Vous pourriez être en mesure de modifier cette méthode AreEquals() pour qu’elle soit une méthode d’extension sur DirectoryInfo si vous le souhaitez, de sorte que vous puissiez vous contenter de myDirectoryInfo.IsEquals(myOtherDirectoryInfo);

 using System; using System.Diagnostics; using System.IO; using System.Collections.Generic; namespace ConsoleApplication3 { class Program { static void Main(ssortingng[] 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 folder1Parents = new List(); List folder2Parents = new List(); 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; } } } 

Vous pouvez utiliser Minimatch, un port de minimatch de 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); 

Voir pourquoi l’option AllowWindowsPaths = true est nécessaire:

Sur les chemins de style Windows, la syntaxe de Minimatch a été conçue pour les chemins de style Linux (avec des barres obliques uniquement). En particulier, il utilise la barre oblique inverse en tant que caractère d’échappement, de sorte qu’il ne peut pas simplement accepter les chemins de style Windows. Ma version C # conserve ce comportement.

Pour supprimer cela et autoriser à la fois des barres obliques inverses et des barres obliques comme séparateurs de chemin d’access (dans les modèles ou les entrées), définissez l’option AllowWindowsPaths :

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

Passer cette option désactivera complètement les caractères d’échappement.

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

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

 bool equals = myDirectoryInfo1.FullName == myDirectoryInfo2.FullName; 

?

 using System; using System.Collections.Generic; using System.Text; namespace EventAnalysis.IComparerImplementation { public sealed class FSChangeElemComparerByPath : IComparer { 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(ssortingng ssortingngA, ssortingng ssortingngB) { int length = 0; int start = 0; List valueA = new List(); List valueB = new List(); ListInit(ref valueA, ssortingngA); ListInit(ref valueB, ssortingngB); 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 ssortingngCollection, ssortingng ssortingngToList) { foreach (ssortingng s in ssortingngToList.Remove(0, 2).Split('\\')) { ssortingngCollection.Add(s); } } private int RecursiveComparing(List valueA, List valueB, int length, int start) { int result = 0; if (start != length) { if (valueA[start] == valueB[start]) { result = RecursiveComparing(valueA, valueB, length, ++start); } else { result = Ssortingng.Compare(valueA[start], valueB[start]); } } else return 0; return result; } } } 
 bool Equals(ssortingng path1, ssortingng path2) { return new Uri(path1) == new Uri(path2); } 

Le constructeur Uri normalise le chemin.