C# Best way to get folder depth for a given path?

2020-07-03 07:12发布

问题:

I'm working on something that requires traversing through the file system and for any given path, I need to know how 'deep' I am in the folder structure. Here's what I'm currently using:

int folderDepth = 0;
string tmpPath = startPath;

while (Directory.GetParent(tmpPath) != null) 
{
    folderDepth++;
    tmpPath = Directory.GetParent(tmpPath).FullName;
}
return folderDepth;

This works but I suspect there's a better/faster way? Much obliged for any feedback.

回答1:

Off the top of my head:

Directory.GetFullPath().Split("\\").Length;


回答2:

I'm more than late on this but I wanted to point out Paul Sonier's answer is probably the shortest but should be:

 Path.GetFullPath(tmpPath).Split(Path.DirectorySeparatorChar).Length;


回答3:

I'm always a fan the recursive solutions. Inefficient, but fun!

public static int FolderDepth(string path)
{
    if (string.IsNullOrEmpty(path))
        return 0;
    DirectoryInfo parent = Directory.GetParent(path);
    if (parent == null)
        return 1;
    return FolderDepth(parent.FullName) + 1;
}

I love the Lisp code written in C#!

Here's another recursive version that I like even better, and is probably more efficient:

public static int FolderDepth(string path)
{
    if (string.IsNullOrEmpty(path))
        return 0;
    return FolderDepth(new DirectoryInfo(path));
}

public static int FolderDepth(DirectoryInfo directory)
{
    if (directory == null)
        return 0;
    return FolderDepth(directory.Parent) + 1;
}

Good times, good times...



回答4:

Assuming your path has already been vetted for being valid, in .NET 3.5 you could also use LINQ to do it in 1 line of code...

Console.WriteLine(@"C:\Folder1\Folder2\Folder3\Folder4\MyFile.txt".Where(c => c = @"\").Count);



回答5:

If you use the members of the Path class, you can cope with localizations of the path separation character and other path-related caveats. The following code provides the depth (including the root). It's not robust to bad strings and such, but it's a start for you.

int depth = 0;
do
{
    path = Path.GetDirectoryName(path);
    Console.WriteLine(path);
    ++depth;
} while (!string.IsNullOrEmpty(path));

Console.WriteLine("Depth = " + depth.ToString());


回答6:

If the directory has a backslash at the end, you get a different answer than when it doesn't. Here's a robust solution to the problem.

string pathString = "C:\\temp\\"
var rootFolderDepth = pathString.Split(Path.DirectorySeparatorChar).Where(i => i.Length > 0).Count();

This returns a path length of 2. If you do it without the where statement, you get a path length of 3 or a path length of 2 if you omit the last separator character.



回答7:

Maybe someone need also some performance testing...

        double linqCountTime = 0;
        double stringSplitTime = 0;
        double stringSplitRemEmptyTime = 0;
        int linqCountFind = 0;
        int stringSplitFind = 0;
        int stringSplitRemEmptyFind = 0;

        string pth = @"D:\dir 1\complicated dir 2\more complicated dir 3\much more complicated dir 4\only dir\another complicated dir\dummy\dummy.dummy.45682\";

        //Heat Up
        DateTime dt = DateTime.Now;
        for (int i = 0; i < 10000; i++)
        {
            linqCountFind = pth.Count(c => c == '\\');
        }
         _= DateTime.Now.Subtract(dt).TotalMilliseconds;
        dt = DateTime.Now;
        for (int i = 0; i < 10000; i++)
        {
            stringSplitFind = pth.Split('\\').Length;
        }
        _ = DateTime.Now.Subtract(dt).TotalMilliseconds;
        dt = DateTime.Now;
        for (int i = 0; i < 10000; i++)
        {
            stringSplitRemEmptyFind = pth.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries).Length;
        }
        _ = DateTime.Now.Subtract(dt).TotalMilliseconds;
        dt = DateTime.Now;

        //Testing
        dt = DateTime.Now;
        for (int i = 0; i < 1000000; i++)
        {
            linqCountFind = pth.Count(c => c == '\\');
        }
        linqCountTime = DateTime.Now.Subtract(dt).TotalMilliseconds; //linq.Count: 1390 ms

        dt = DateTime.Now;
        for (int i = 0; i < 1000000; i++)
        {
            stringSplitFind = pth.Split('\\').Length-1;
        }
        stringSplitTime = DateTime.Now.Subtract(dt).TotalMilliseconds; //string.Split: 715 ms

        dt = DateTime.Now;
        for (int i = 0; i < 1000000; i++)
        {
            stringSplitRemEmptyFind = pth.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries).Length;
        }
        stringSplitRemEmptyTime = DateTime.Now.Subtract(dt).TotalMilliseconds; // string.Split with RemoveEmptyEntries option: 720 ms

        string linqCount = "linqCount - Find: "+ linqCountFind + "; Time: "+ linqCountTime.ToString("F0") +" ms"+ Environment.NewLine;
        string stringSplit = "stringSplit - Find: " + stringSplitFind + "; Time: " + stringSplitTime.ToString("F0") + " ms" + Environment.NewLine;
        string stringSplitRemEmpty = "stringSplitRemEmpty - Find: " + stringSplitRemEmptyFind + "; Time: " + stringSplitRemEmptyTime.ToString("F0") + " ms" + Environment.NewLine;

        MessageBox.Show(linqCount + stringSplit + stringSplitRemEmpty);

        // Results:
        // linqCount - Find: 9;  Time: 1390 ms
        // stringSplit - Find: 9;  Time: 715 ms
        // stringSplitRemEmpty - Find: 9;  Time: 720 ms
  • So, for most cases, the best is string.split() (see results in code comments).
  • string.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries) is safer for path.
  • And for more complicated cases see: https://cc.davelozinski.com/c-sharp/fastest-way-count-number-times-character-occurs-string and https://cc.davelozinski.com/c-sharp/c-net-fastest-way-count-substring-occurrences-string