给定完整路径,检查路径是否是某个其他路径的子目录,否则

我有2个字符串–dir1和dir2,我需要检查一个是否是其他的子目录。 我尝试使用Contains方法:

dir1.contains(dir2); 

但是如果目录具有相似的名称,那也会返回true,例如 – c:\abcc:\abc1不是子目录,bet返回true。 肯定有更好的办法。

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

或者在循环中允许嵌套的子目录,即C:\ foo \ bar \ bazC:\ 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; } 
  • 不区分大小写
  • 容忍\/文件夹分隔符的混合
  • 容忍..\在路径
  • 避免匹配部分文件夹名称( c:\foobar不是c:\foo的子路径)

注意:这仅匹配路径字符串,不适用于文件系统中的符号链接和其他类型的链接。

码:

 public static class StringExtensions { ///  /// Returns true if  starts with the path . /// 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"). ///  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); } ///  /// Returns  with the minimal concatenation of  (starting from end) that /// results in satisfying .EndsWith(ending). ///  /// "hel".WithEnding("llo") returns "hello", which is the result of "hel" + "lo". 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; } /// Gets the rightmost  characters from a string. /// The string to retrieve the substring from. /// The number of characters to retrieve. /// The substring. 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; } } 

测试用例(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); } } 

更新

  • 2015-08-18:修复部分文件夹名称的错误匹配。 添加测试用例。
  • 2015-09-02:支持..\在路径中,添加缺少的代码
  • 2017-09-06:在符号链接上添加注释。

尝试:

 dir1.contains(dir2+"\\"); 

我的路径可能包含不同的shell,甚至还有未经修剪的段……这似乎有效:

 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 SegmentizePath(string path) { var segments = new List(); var remaining = new DirectoryInfo(path); while (null != remaining) { segments.Add(remaining.Name); remaining = remaining.Parent; } segments.Reverse(); return segments; } 

根据@ BrokenGlass的答案,但调整:

 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 { 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)); } } 

如果性能至关重要,则不理想。

更新 – 我最初写的是错误的(见下文):

在我看来,你实际上坚持使用.StartsWith()函数进行基本的字符串比较(当然使用.ToLower()),以及计算路径分隔符,但是你还需要额外考虑一下路径分隔符 – 您需要事先在字符串上使用类似Path.GetFullPath()的东西,以确保您处理一致的路径字符串格式。 所以你最终会得到一些基本而简单的东西,比如:

 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 } } 

更新中…

正如我在使用我的代码时发现的那样,这是错误的原因,因为它没有考虑一个目录名以与另一个目录的整个名称相同的字符开头的情况。 我有一个案例,我有一个目录路径“D:\ prog \ dat \ Mirror_SourceFiles”和另一个目录路径“D:\ prog \ dat \ Mirror”。 由于我的第一条路径确实“以”开头“字母”D:\ prog \ dat \ Mirror“,我的代码给了我一个错误的匹配。 我完全摆脱了.StartsWith并将代码改为此(方法:将路径分成各个部分,并将部分与较少数量的部分进行比较):

 // make sure "dir1" and "dir2a" are distinct from each other // (ie, 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 } 

当然,请注意,在“真实程序”中,您将在try-catch中使用Path.GetFullPath()函数来处理与您传递给它的字符串相关的exception。

在我的例子中,路径和可能的子路径不包含’..’并且永远不会以’\’结尾:

 private static bool IsSubpathOf(string path, string subpath) { return (subpath.Equals(path, StringComparison.OrdinalIgnoreCase) || subpath.StartsWith(path + @"\", StringComparison.OrdinalIgnoreCase)); } 
 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); }