1

in a folder, I have these files. the order on windows explorer is:

  • input (1).jpg
  • input (2).jpg
  • input (101).jpg

This is the way I got the files from the folder:

var files = Directory.GetFiles(inputImage, "*.*", SearchOption.TopDirectoryOnly);

But the order will be:

  • input (1).jpg
  • input (101).jpg
  • input (2).jpg

Even I tried: Array.Sort(files);

But the order is the same, How can I load the files with this order:

  • input (1).jpg
  • input (2).jpg
  • input (101).jpg
halfer
  • 19,824
  • 17
  • 99
  • 186
olidev
  • 20,058
  • 51
  • 133
  • 197
  • 1
    Natural sorting http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html – Mr47 Jun 18 '12 at 14:55

7 Answers7

2

You'll need to implement your own comparison to compare two strings in a way which "realizes" that they contain embedded non-zero-padded numbers. Explorer contains this logic already, and (as per the comment) this is exposed in StrCmpLogicalW which you could call with P/Invoke in a custom Comparer<string> implementation which you could pass to a sorting method.

If you know everything else about the name (i.e. the pattern) then it's easy to strip out the fixed parts for the comparison - but StrCmpLogicalW will give you a nice general implementation.

Of course if you're in control of whatever's creating the files, a simpler approach is to zero-pad the filenames to some reasonable number of digits, e.g. 5 or 6.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

Keep in mind that Array.Sort(files) is, by default, sorting file names as text, not as numbers. Try parsing the numbers and then sorting by those.

Steve Konves
  • 2,648
  • 3
  • 25
  • 44
2

Well I'll try something like this:

        var files = from file in directoryInfo.EnumerateFiles()
        .Where(f => f.Extension == ".jpg")
                    orderby ExtractNumber(file.Name) ascending
                    select file.FullName;
        return files.ToList<string>();

Here we extract the file number -between the ( and )-.

private static object ExtractNumber(string p)
{
    int lenght = p.IndexOf(')') - p.IndexOf('(');
    return int.Parse(p.Substring(p.IndexOf('('), lenght - 1)); 
}

I'm pretty sure there must be an efficient way to do this, but just my two cents.

Erre Efe
  • 15,387
  • 10
  • 45
  • 77
2

If you load the files in a simple way:

string[] files = Directory.GetFiles ("folder");

and assuming that there is always a number in parentheses you can use a function:

Array.Sort (files, compareFilesWithBrackets);

with a comparison function:

public static int compareFilesWithBrackets (string arg1, string arg2)
{
        string filename1 = System.IO.Path.GetFileName (arg1); //extract filename from the path of the first file
        string filename2 = System.IO.Path.GetFileName (arg2); // extract second file filename
        Match m = Regex.Match (filename1, "\\(([0-9]+)\\)"); // check if there is (number) for file 1
        int file1 = 0; // if no number leave 0
        if (m.Success) { // else if success parse the number from brackets
            file1 = int.Parse (m.Groups [1].Value); 
        }
        m = Regex.Match (filename2, "\\(([0-9]+)\\)");
        int file2 = 0;
        if (m.Success) {
            file2 = int.Parse (m.Groups [1].Value); 
        }
        return file1 - file2;
}
Bartosz Pachołek
  • 1,278
  • 1
  • 8
  • 17
1

If it were me, I'd create a dictionary object and store the first value as the original filename, and the 2nd as a custom filename that you will need to massage.

So, for instance, the dictionary object would look something like this:

input (1).jpg    |  input (001).jpg
input (2).jpg    |  input (002).jpg
input (101).jpg  |  input (101).jpg

Then you can sort on the 2nd field, but pull the filename from the first field in the dictionary object.

Scottie
  • 11,050
  • 19
  • 68
  • 109
1

You can use the OrderBy extension, more details here http://msdn.microsoft.com/en-us/library/bb534966.aspx

It's a simple lambda expression the t=> t part. You can do some manipulation before it gets sorted if need be.

var files = Directory.GetFiles(inputImage, "*.*", SearchOption.TopDirectoryOnly).OrderBy(t => t);

If you wanted to sort only on the number, you could some some clever string substitution like

.OrderBy(t => t.Replace("input", "").Replace("(", "").Replace(")","").Trim());
Breland
  • 183
  • 1
  • 11
  • I tried this within Replace(".jpg", "") but it does not work :p – olidev Jun 18 '12 at 15:22
  • Ah, should have cast it to an int, eg .OrderBy(t => int.Parse(t.Replace("input", "").Replace("(", "").Replace(")","").Trim())); – Breland Jun 19 '12 at 13:13
  • Really, you could have just used a regular expression to grab the number, and ordered it on that. – Breland Jun 19 '12 at 13:15
1

This CustomSort can help

List<string> list = new List<string>()
{
   "input (1).jpg","input (101).jpg", "input (11).jpg", "input (2).jpg"
};

var sortedList = list.CustomSort().ToList();

Output will be:

input (1).jpg
input (2).jpg
input (11).jpg
input (101).jpg

I'll copy the code here

public static class MyExtensions
{
    public static IEnumerable<string> CustomSort(this IEnumerable<string> list)
    {
        int maxLen = list.Select(s => s.Length).Max();

        return list.Select(s => new
        {
            OrgStr = s,
            SortStr = Regex.Replace(s, @"(\d+)|(\D+)", m => m.Value.PadLeft(maxLen, char.IsDigit(m.Value[0]) ? ' ' : '\xffff'))
        })
        .OrderBy(x => x.SortStr)
        .Select(x => x.OrgStr);
    }
}
Community
  • 1
  • 1
L.B
  • 114,136
  • 19
  • 178
  • 224