web-dev-qa-db-fra.com

Comment comparer des chemins (répertoires) en C #?

Si j'ai deux objets 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\.

Les éléments suivants peuvent ou non correspondre à C:\temp:

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

S'il est important de considérer le répertoire de travail actuel, je peux le déterminer moi-même, donc ce n'est pas si important. Les points de fuite sont supprimés dans les fenêtres, donc ces chemins doivent être identiques, mais ils ne le sont pas sous unix. Par conséquent, sous mono, j'attendrais d'autres résultats.

La sensibilité à la casse est facultative. Les chemins peuvent ou non exister, et l'utilisateur peut ou non avoir des autorisations sur le chemin. Je préférerais une méthode rapide et robuste qui ne nécessite aucune E/S (donc aucune vérification des autorisations), mais s'il y a quelque chose construit -dans Je serais heureux avec quelque chose "assez bon" aussi ...

68
Eamon Nerbonne

De cette réponse , cette méthode peut traiter quelques cas Edge:

public static string NormalizePath(string 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 fichiers et de répertoires.

28
nawfal

GetFullPath semble faire le travail, sauf en cas de différence de casse (Path.GetFullPath("test") != Path.GetFullPath("TEST")) et de fin de barre oblique . Ainsi, le code suivant devrait fonctionner correctement:

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

Ou, si vous voulez commencer par DirectoryInfo:

String.Compare(
    dirinfo1.FullName.TrimEnd('\\'),
    dirinfo2.FullName.TrimEnd('\\'), 
    StringComparison.InvariantCultureIgnoreCase)
90
VladV

Il y a quelques brèves à l'implémentation de chemins dans .NET. Il y a beaucoup de plaintes à ce sujet. Patrick Smacchia , le créateur de NDepend, a publié une bibliothèque source ouverte permettant le traitement des opérations de chemin communes et complexes . Si vous effectuez beaucoup d'opérations de comparaison sur les chemins de votre application, cette bibliothèque peut vous être utile.

11
Steven
 System.IO.Path.GetFullPath(pathA).Equals(System.IO.Path.GetFullPath(PathB));
3
Asad Butt

Il semble que P/Invoking GetFinalPathNameByHandle () serait la solution la plus fiable. 

UPD: Oups, je n'ai pas pris en compte votre désir de ne pas utiliser d'I/O

3
Igor Korkhov

Microsoft a mis en œuvre des méthodes similaires, bien qu'elles ne soient pas aussi utiles que les réponses ci-dessus:

2
Chris Xue

Vous pouvez utiliser Minimatch, un port du 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 inversée comme caractère d'échappement, de sorte qu'il ne peut pas simplement accepter les chemins d'accès de style Windows. Ma version C # conserve ce comportement.

Pour supprimer cela et autoriser les barres obliques inverses et les barres obliques inversées comme séparateurs de chemin (dans les modèles ou en entrée), 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

1
vulcan raven

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 pourrez peut-être utiliser une méthode récursive pour examiner les propriétés Name de chaque parent en fonction de vos deux classes DirectoryInfo afin de vous assurer que le chemin complet est identique.

EDIT: est-ce que cela fonctionne pour votre situation? Créez une application console et collez-la dans l'intégralité du fichier Program.cs. Fournissez deux objets DirectoryInfo à la fonction AreEquals () et elle renverra True si ce sont les mêmes répertoires. Vous pourriez peut-être modifier cette méthode AreEquals() pour qu’elle soit une méthode d’extension sur DirectoryInfo si vous le souhaitez, afin que vous puissiez simplement faire 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;
        }
    }
}
1
Andy Shellam
bool Equals(string path1, string path2)
{
    return new Uri(path1) == new Uri(path2);
}

Le constructeur Uri normalise le chemin.

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

?

0
herzmeister
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;
        }
    }
}
0
Denis