Avec le chemin complet, vérifiez si le chemin est un sous-répertoire d'un autre chemin, ou autrement

j'ai 2 chaînes - dir1 et dir2, et je dois vérifier si l'une est sous-répertoire pour l'autre. J'ai essayé d'aller avec la méthode contains:

dir1.contains(dir2);

mais qui retourne aussi true, si les répertoires ont des noms similaires, par exemple - c:abc et c:abc1 ne sont pas des sous-répertoires, bet retourne true. Il doit y avoir une meilleure façon.

24
demandé sur ArunPratap 2011-04-11 10:00:02

8 réponses

DirectoryInfo di1 = new DirectoryInfo(dir1);
DirectoryInfo di2 = new DirectoryInfo(dir2);
bool isParent = di2.Parent.FullName == di1.FullName;

ou dans une boucle pour permettre des sous-répertoires imbriqués, i.e. C:\foo\bar\baz est un sous-répertoire de C:\foo:

DirectoryInfo di1 = new DirectoryInfo(dir1);
DirectoryInfo di2 = new DirectoryInfo(dir2);
bool isParent = false;
while (di2.Parent != null)
{
    if (di2.Parent.FullName == di1.FullName)
    {
        isParent = true;
        break;
    }
    else di2 = di2.Parent;
}
25
répondu BrokenGlass 2011-04-11 06:09:13
  • insensible à la casse
  • Tolère un mélange de \ et / délimiteurs de dossiers
  • Tolère ..\ dans le chemin d'accès
  • évite les correspondances sur les noms de dossiers partiels (c:\foobar pas un subpath de c:\foo)

Note: cela ne correspond qu'à la chaîne de chemins et ne fonctionne pas pour les liens symboliques et autres types de liens dans le système de fichiers.

Code:

public static class StringExtensions
{
    /// <summary>
    /// Returns true if <paramref name="path"/> starts with the path <paramref name="baseDirPath"/>.
    /// The comparison is case-insensitive, handles / and \ slashes as folder separators and
    /// only matches if the base dir folder name is matched exactly ("c:\foobar\file.txt" is not a sub path of "c:\foo").
    /// </summary>
    public static bool IsSubPathOf(this string path, string baseDirPath)
    {
        string normalizedPath = Path.GetFullPath(path.Replace('/', '\')
            .WithEnding("\"));

        string normalizedBaseDirPath = Path.GetFullPath(baseDirPath.Replace('/', '\')
            .WithEnding("\"));

        return normalizedPath.StartsWith(normalizedBaseDirPath, StringComparison.OrdinalIgnoreCase);
    }

    /// <summary>
    /// Returns <paramref name="str"/> with the minimal concatenation of <paramref name="ending"/> (starting from end) that
    /// results in satisfying .EndsWith(ending).
    /// </summary>
    /// <example>"hel".WithEnding("llo") returns "hello", which is the result of "hel" + "lo".</example>
    public static string WithEnding([CanBeNull] this string str, string ending)
    {
        if (str == null)
            return ending;

        string result = str;

        // Right() is 1-indexed, so include these cases
        // * Append no characters
        // * Append up to N characters, where N is ending length
        for (int i = 0; i <= ending.Length; i++)
        {
            string tmp = result + ending.Right(i);
            if (tmp.EndsWith(ending))
                return tmp;
        }

        return result;
    }

    /// <summary>Gets the rightmost <paramref name="length" /> characters from a string.</summary>
    /// <param name="value">The string to retrieve the substring from.</param>
    /// <param name="length">The number of characters to retrieve.</param>
    /// <returns>The substring.</returns>
    public static string Right([NotNull] this string value, int length)
    {
        if (value == null)
        {
            throw new ArgumentNullException("value");
        }
        if (length < 0)
        {
            throw new ArgumentOutOfRangeException("length", length, "Length is less than zero");
        }

        return (length < value.Length) ? value.Substring(value.Length - length) : value;
    }
}

des cas de Test (NUnit):

[TestFixture]
public class StringExtensionsTest
{
    [TestCase(@"c:\foo", @"c:", Result = true)]
    [TestCase(@"c:\foo", @"c:\", Result = true)]
    [TestCase(@"c:\foo", @"c:\foo", Result = true)]
    [TestCase(@"c:\foo", @"c:\foo\", Result = true)]
    [TestCase(@"c:\foo\", @"c:\foo", Result = true)]
    [TestCase(@"c:\foo\bar\", @"c:\foo\", Result = true)]
    [TestCase(@"c:\foo\bar", @"c:\foo\", Result = true)]
    [TestCase(@"c:\foo\a.txt", @"c:\foo", Result = true)]
    [TestCase(@"c:\FOO\a.txt", @"c:\foo", Result = true)]
    [TestCase(@"c:/foo/a.txt", @"c:\foo", Result = true)]
    [TestCase(@"c:\foobar", @"c:\foo", Result = false)]
    [TestCase(@"c:\foobar\a.txt", @"c:\foo", Result = false)]
    [TestCase(@"c:\foobar\a.txt", @"c:\foo\", Result = false)]
    [TestCase(@"c:\foo\a.txt", @"c:\foobar", Result = false)]
    [TestCase(@"c:\foo\a.txt", @"c:\foobar\", Result = false)]
    [TestCase(@"c:\foo\..\bar\baz", @"c:\foo", Result = false)]
    [TestCase(@"c:\foo\..\bar\baz", @"c:\bar", Result = true)]
    [TestCase(@"c:\foo\..\bar\baz", @"c:\barr", Result = false)]
    public bool IsSubPathOfTest(string path, string baseDirPath)
    {
        return path.IsSubPathOf(baseDirPath);
    }
}

mise à jour

  • 2015-08-18: Correction de la correspondance des bogues sur les noms de dossiers partiels. Ajouter des cas de test.
  • 2015-09-02: Soutien ..\ dans les chemins, ajouter le code manquant
  • 2017-09-06: ajouter une note sur les liens symboliques.
20
répondu angularsen 2017-09-06 04:34:30

Essaie:

dir1.contains(dir2+"\");
3
répondu Andrew Cooper 2011-04-11 06:11:27

mes chemins pourraient éventuellement contenir des boîtiers différents et même avoir des segments non tronqués... Cela semble fonctionner:

public static bool IsParent(string fullPath, string base)
{
	var fullPathSegments = SegmentizePath(fullPath);
	var baseSegments = SegmentizePath(base);
	var index = 0;
	while (fullPathSegments.Count>index && baseSegments.Count>index && 
		fullPathSegments[index].Trim().ToLower() == baseSegments[index].Trim().ToLower())
		index++;
	return index==baseSegments.Count-1;
}

public static IList<string> SegmentizePath(string path)
{
	var segments = new List<string>();
	var remaining = new DirectoryInfo(path);
	while (null != remaining)
	{
		segments.Add(remaining.Name);
		remaining = remaining.Parent;
	}
	segments.Reverse();
	return segments;
}
0
répondu AlexeiOst 2014-11-13 02:49:35
using System.IO;

internal static class DirectoryInfoExt
{
    internal static bool IsSubDirectoryOfOrSame(this DirectoryInfo directoryInfo, DirectoryInfo potentialParent)
    {
        if (DirectoryInfoComparer.Default.Equals(directoryInfo, potentialParent))
        {
            return true;
        }

        return IsStrictSubDirectoryOf(directoryInfo, potentialParent);
    }

    internal static bool IsStrictSubDirectoryOf(this DirectoryInfo directoryInfo, DirectoryInfo potentialParent)
    {
        while (directoryInfo.Parent != null)
        {
            if (DirectoryInfoComparer.Default.Equals(directoryInfo.Parent, potentialParent))
            {
                return true;
            }

            directoryInfo = directoryInfo.Parent;
        }

        return false;
    }
}

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

public class DirectoryInfoComparer : IEqualityComparer<DirectoryInfo>
{
    private static readonly char[] TrimEnd = { '\' };
    public static readonly DirectoryInfoComparer Default = new DirectoryInfoComparer();
    private static readonly StringComparer OrdinalIgnoreCaseComparer = StringComparer.OrdinalIgnoreCase;

    private DirectoryInfoComparer()
    {
    }

    public bool Equals(DirectoryInfo x, DirectoryInfo y)
    {
        if (ReferenceEquals(x, y))
        {
            return true;
        }

        if (x == null || y == null)
        {
            return false;
        }

        return OrdinalIgnoreCaseComparer.Equals(x.FullName.TrimEnd(TrimEnd), y.FullName.TrimEnd(TrimEnd));
    }

    public int GetHashCode(DirectoryInfo obj)
    {
        if (obj == null)
        {
            throw new ArgumentNullException(nameof(obj));
        }
        return OrdinalIgnoreCaseComparer.GetHashCode(obj.FullName.TrimEnd(TrimEnd));
    }
}

pas idéal si la performance est essentielle.

0
répondu Johan Larsson 2015-10-31 20:22:07

mise à Jour - ce que j'ai écrit à l'origine est mal (voir ci-dessous):

il me semble que vous vous en tenez en fait à la comparaison de base des chaînes (en utilisant .ToLower() de cours) à l'aide de l' .La fonction StartsWith (), en même temps que le comptage des séparateurs de chemin, mais vous ajoutez une considération supplémentaire en ce qui concerne le nombre de séparateurs de chemin - et vous devez employer quelque chose comme le chemin.GetFullPath() sur les cordes à l'avance pour vous assurer que vous faites affaire avec cohérence chemin formats de chaîne. Donc vous finiriez avec quelque chose de basique et simple, comme ceci:

string dir1a = Path.GetFullPath(dir1).ToLower();
string dir2a = Path.GetFullPath(dir2).ToLower();
if (dir1a.StartsWith(dir2a) || dir2a.StartsWith(dir1a)) {
    if (dir1a.Count(x => x = Path.PathSeparator) != dir2a.Count(x => x = Path.PathSeparator)) {
        // one path is inside the other path
    }
}

mise à Jour...

comme j'ai découvert en utilisant mon code, la raison pour laquelle c'est faux, est qu'il ne tient pas compte des cas où un nom de répertoire commence avec les mêmes caractères que le nom complet de l'autre répertoire. J'ai eu un cas où j'ai eu un chemin de répertoire de "D:\prog\dat\Mirror_SourceFiles" and another directory path of "D:\prog\dat\Mirror". Depuis mon premier chemin est-ce qu'en effet "commence par" les lettres "D:\prog\dat\Mirror" mon code m'a donné une fausse correspondance. Je me suis débarrassé de .Commence entièrement et change le code en ceci (méthode: divise le chemin vers les différentes parties, et compare les parties jusqu'au plus petit nombre de parties):

// make sure "dir1" and "dir2a" are distinct from each other
// (i.e., not the same, and neither is a subdirectory of the other)
string[] arr_dir1 = Path.GetFullPath(dir1).Split(Path.DirectorySeparatorChar);
string[] arr_dir2 = Path.GetFullPath(dir2).Split(Path.DirectorySeparatorChar);
bool bSame = true;
int imax = Math.Min(arr_dir1.Length, arr_dir2.Length);
for (int i = 0; i < imax; ++i) {
  if (String.Compare(arr_dir1[i], arr_dir2[i], true) != 0) {
    bSame = false;
    break;
  }
}

if (bSame) {
  // do what you want to do if one path is the same or
  // a subdirectory of the other path
}
else {
  // do what you want to do if the paths are distinct
}

bien sûr, notez que dans un "vrai programme" vous allez utiliser le chemin.La fonction GetFullPath () dans un try-catch pour gérer les exceptions appropriées en ce qui concerne la chaîne que vous lui passez.

0
répondu Steve Greene 2015-12-12 00:14:09

dans mon cas le chemin et le sous-chemin possible ne contiennent pas '.."et jamais de fin dans '\':

private static bool IsSubpathOf(string path, string subpath)
{
    return (subpath.Equals(path, StringComparison.OrdinalIgnoreCase) ||
            subpath.StartsWith(path + @"\", StringComparison.OrdinalIgnoreCase));
}
0
répondu Tal Aloni 2017-08-28 08:35:00
public static bool IsSubpathOf(string rootPath, string subpath)
{
    if (string.IsNullOrEmpty(rootPath))
        throw new ArgumentNullException("rootPath");
    if (string.IsNullOrEmpty(subpath))
        throw new ArgumentNulLException("subpath");
    Contract.EndContractBlock();

    return subath.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase);
}
-1
répondu John Whiter 2015-07-31 08:01:21