ツ.jp
ツ.jp

Reputation: 315

Sort FileSystemInfo[] by Name

I have probably spent about 500 hours Googling this and reading MSDN documentation and it still refuses to work the way I want.

I can sort by name for files like this:

01.png
02.png
03.png
04.png

I.e. all the same file length.

The second there is a file with a longer file length everything goes to hell.

For example in the sequence:

1.png
2.png
3.png
4.png
5.png
10.png
11.png

It reads:

1.png, 2.png then 10.png, 11.png

I don't want this.

My Code:

DirectoryInfo di = new DirectoryInfo(directoryLoc);
FileSystemInfo[] files = di.GetFileSystemInfos("*." + fileExtension);
Array.Sort<FileSystemInfo>(files, new Comparison<FileSystemInfo>(compareFiles));

foreach (FileInfo fri in files)
{
    fri.MoveTo(directoryLoc + "\\" + prefix + "{" + operationNumber.ToString() + "}" + (i - 1).ToString("D10") +
        "." + fileExtension);

    i--;
    x++;
    progressPB.Value = (x / fileCount) * 100;
}

// compare by file name
int compareFiles(FileSystemInfo a, FileSystemInfo b)
{
    // return a.LastWriteTime.CompareTo(b.LastWriteTime);
    return a.Name.CompareTo(b.Name);
}

Upvotes: 2

Views: 1755

Answers (5)

spender
spender

Reputation: 120518

How about a bit of linq and regex to fix the ordering?

var orderedFileSysInfos = 
  new DirectoryInfo(directoryloc)
    .GetFileSystemInfos("*." + fileExtension)
    //regex below grabs the first bunch of consecutive digits in file name
    //you might want something different
    .Select(fsi => new{fsi, match = Regex.Match(fsi.Name, @"\d+")})
    //filter away names without digits
    .Where(x => x.match.Success)
    //parse the digits to int
    .Select(x => new {x.fsi, order = int.Parse(x.match.Value)})
    //use this value to perform ordering
    .OrderBy(x => x.order)
    //select original FileSystemInfo
    .Select(x => x.fsi)
    //.ToArray() //maybe?

Upvotes: 0

Chris Gessler
Chris Gessler

Reputation: 23123

I ran into this same issue, but instead of sorting the list myself, I changed the filename by using 6 digit '0' padded key.

My list now looks like this:

000001.jpg
000002.jpg
000003.jpg
...
000010.jpg

But, if you can't change the filenames, you're going to have to implement your own sorting routine to deal with the alpha sort.

Upvotes: 0

adelphus
adelphus

Reputation: 10316

You're comparing the names as strings, even though (I'm assuming) you want them sorted by number.

This is a well-known problem where "10" comes before "9" because the first character in 10 (1) is less than the first character in 9.

If you know that the files will all consist of numbered names, you can modify your custom sort routine to convert the names to integers and sort them appropriately.

Upvotes: 3

Your code is correct and working as expected, just the sort is performed alphabetically, not numerically.

For instance, the strings "1", "10", "2" are in alphabetical order. Instead if you know your filenames are always just a number plus ".png" you can do the sort numerically. For instance, something like this:

int compareFiles(FileSystemInfo a, FileSystemInfo b)         
{             
    // Given an input 10.png, parses the filename as integer to return 10
    int first = int.Parse(Path.GetFileNameWithoutExtension(a.Name));
    int second = int.Parse(Path.GetFileNameWithoutExtension(b.Name));

    // Performs the comparison on the integer part of the filename
    return first.CompareTo(second);
}

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1503220

It's not a matter of the file length particularly - it's a matter of the names being compared in lexicographic order.

It sounds like in this particular case you want to get the name without the extension, try to parse it as an integer, and compare the two names that way - you could fall back to lexicographic ordering if that fails.

Of course, that won't work if you have "debug1.png,debug2.png,...debug10.png"...you'd need a more sophisticated algorithm in that case.

Upvotes: 3

Related Questions