Let say I have the following folders:
New Folder
- New Folder
- New Folder (2)
- New Folder (3)
- New Folder (4)
New Fold开发者_JAVA技巧er (2)
New Folder (3)
New Folder (4)
And a query
from s in Directory.GetDirectories(@"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
select s
The results:
D:\Project\uploads\New Folder
D:\Project\uploads\New Folder (2)
D:\Project\uploads\New Folder (3)
D:\Project\uploads\New Folder (4)
D:\Project\uploads\New Folder\New Folder
D:\Project\uploads\New Folder\New Folder (2)
D:\Project\uploads\New Folder\New Folder (3)
D:\Project\uploads\New Folder\New Folder (4)
Is there anyway to sort the list to the right order? I expected it to be:
D:\Project\uploads\New Folder
D:\Project\uploads\New Folder\New Folder
D:\Project\uploads\New Folder\New Folder (2)
D:\Project\uploads\New Folder\New Folder (3)
D:\Project\uploads\New Folder\New Folder (4)
D:\Project\uploads\New Folder (2)
D:\Project\uploads\New Folder (3)
D:\Project\uploads\New Folder (4)
Any helps would be appreciated!
This wasn't as trivial as I thought. Probably the most sane solution (aside from building the list recursively) is to implement a comparer for this to do the sorting.
class DirectorySorter : IComparer<string>
{
public int Compare(string x, string y)
{
return StringComparer.Ordinal.Compare(x.Replace(Path.DirectorySeparatorChar, '\0'),
y.Replace(Path.DirectorySeparatorChar, '\0'));
var xPaths = x.Split(Path.DirectorySeparatorChar);
var yPaths = y.Split(Path.DirectorySeparatorChar);
var minLength = Math.Min(xPaths.Length, yPaths.Length);
for (int i = 0; i < minLength; i++)
{
var ires = xPaths[i].CompareTo(yPaths[i]);
if (ires != 0) return ires;
}
var lres = xPaths.Length.CompareTo(yPaths.Length);
if (lres == 0)
{
return lres;
}
else if (lres < 0)
{
var i = y.LastIndexOf(Path.DirectorySeparatorChar);
return x.Length == i ? lres : -lres;
}
else //if (lres > 0)
{
var i = x.LastIndexOf(Path.DirectorySeparatorChar);
return y.Length == i ? lres : -lres;
}
}
}
(Seeing Steck's answer shows that I was nearly there with what I originally had. Just that I needed to use the Ordinal string comparer. So it turns out it works using that change.)
On the other hand, we could use some properties of the directory structure to simplify this task and not implement a comparer.
var query = Directory
.EnumerateDirectories(@"D:\Project\uploads", "*", SearchOption.AllDirectories)
.OrderBy(name => name.Replace(Path.DirectorySeparatorChar, '\0'), StringComparer.Ordinal);
private class Comparer : IComparer<string>
{
public int Compare(string x, string y)
{
return StringComparer.Ordinal.Compare(x.Replace(Path.DirectorySeparatorChar, '\0'),
y.Replace(Path.DirectorySeparatorChar, '\0'));
}
}
and then
var source = Directory.GetDirectories(@"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
var target = source.OrderBy(x => x, new Comparer()).ToArray();
The only thing you need to change about the default ordering is to make sure that the \
character is always treated as the first letter in your alphabet. I don't have an exact answer how to implement this, but:
You can use
order by
clause if you find a way to replace\
in the string with a character that is smaller than all other characters and use this replaced string as the key.You can use
Array.Sort
and implement your string comparer that re-implements string comparison, but encodes this additional rule about the\
character.
With .NET 4.0 try
Directory.EnumerateDirectories(@"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
it might do what you expect. If it doesn't, you can do it explicitly:
Directory.GetDirectories(@"D:\Project\uploads")
.SelectMany(dir => dir.GetDirectories().OrderBy(sub => sub.Name))
Lastly you might do something like:
from s in Directory.GetDirectories(@"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
order by s.Parent.Name, s.Name
select s
from s in Directory.GetDirectories(@"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
let members = s.Name.Split(new [] {Path.SeparatorChar})
order by members[2], s.Name
select s
to get even more control/flexibility. Chose the simplest approach depending on your needs
Thanks for ur comment and answer guys,
I think life'll be much easier with recursive
void Main()
{
string rootFolder = @"D:\Project\uploads";
string[] f = Directory.GetDirectories(rootFolder, "*.*", SearchOption.AllDirectories);
Func<string, string[]> build = null;
build = (p) => {
return (from x in f where Path.GetDirectoryName(x) == p
from y in new string[]{ x }.Union(build(x)) select y).ToArray();
};
f = build(rootFolder).Dump();
}
精彩评论