What is an easy way to check if directory 1 is a subdirectory of directory 2 and vice versa?
I checked the Path and DirectoryInfo helperclasses but found no system-r
this is what I got, after first verifying that the two directory path strings are something and in a path format I know something about: shouldnotbechilddirpath.ToUpper().StartsWith(maybeparentdirpath.ToUpper())
Be sure to take out the ToUppers() if you are maybe working in a case sensitive file system.
You can compare directory2 to directory1's Parent property when using a DirectoryInfo in both cases.
DirectoryInfo d1 = new DirectoryInfo(@"C:\Program Files\MyApp");
DirectoryInfo d2 = new DirectoryInfo(@"C:\Program Files\MyApp\Images");
if(d2.Parent.FullName == d1.FullName)
{
Console.WriteLine ("Sub directory");
}
In response to the first part of the question: "Is dir1 a sub-directory of dir2?", this code should work:
public bool IsSubfolder(string parentPath, string childPath)
{
var parentUri = new Uri(parentPath);
var childUri = new DirectoryInfo(childPath).Parent;
while (childUri != null)
{
if(new Uri(childUri.FullName) == parentUri)
{
return true;
}
childUri = childUri.Parent;
}
return false;
}
The URI
s (on Windows at least, might be different on Mono/Linux) are case-insensitive. If case sensitivity is important, use the Compare
method on Uri
instead.
See original answer here: https://stackoverflow.com/a/31941159/134761
\
and /
folder delimiters..\
in pathc:\foobar
not a subpath of c:\foo
)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;
}
}
Test cases (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);
}
}
Update 2015-08-18: Fix bug matching on partial folder names. Add test cases.
Update 2016-01-29: Link to original question https://stackoverflow.com/a/31941159/134761
With help from the great test cases written in angularsen's answer, I wrote the following simpler extension method on .NET Core 3.1 for Windows:
public static bool IsSubPathOf(this string dirPath, string baseDirPath)
{
dirPath = dirPath.Replace('/', '\\');
if (!dirPath.EndsWith('\\'))
{
dirPath += '\\';
}
baseDirPath = baseDirPath.Replace('/', '\\');
if (!baseDirPath.EndsWith('\\'))
{
baseDirPath += '\\';
}
var dirPathUri = new Uri(dirPath).LocalPath;
var baseDirUri = new Uri(baseDirPath).LocalPath;
return dirPathUri.Contains(baseDirUri, StringComparison.OrdinalIgnoreCase);
}
DirectoryInfo has a property Parent which is also a DirectoryInfo type. You can use that to to determine if your directory is a subdirectory of a parent directory.