Stopbyte

How to make Natural Sort Order in C#

I need to use a natural order sort in C# on a FileInfo Array using IComparer interface.

I get my FileInfo Array using:

var FilesArray = dir.GetFiles();
// where dir is the directory i am accessing.

And I need to know if there are any good resources and examples of how to do natural order sort in C#?

Thanks in advance!

2 Likes

Here is a simple clean natural Sort Order function, only using Regex and without any external libraries or system Dlls Dependencies…etc:

public static IEnumerable<T> OrderByAlphaNumeric<T>(this IEnumerable<T> source, Func<T, string> selector)
{
    int max = source.SelectMany(i => Regex.Matches(selector(i), @"\d+").Cast<Match>().Select(m => (int?)m.Value.Length)).Max() ?? 0;
    return source.OrderBy(i => Regex.Replace(selector(i), @"\d+", m => m.Value.PadLeft(max, '0')));
}

The above pads any numbers in the string to the max length of all numbers in all strings and uses the resulting string to sort.

The cast to (int?) is to allow for collections of strings without any numbers (.Max() on an empty enumerable throws an InvalidOperationException).

Hope that works and helps someone else.

.

2 Likes

I believe that the easiest thing to do is P/Invoke the built-in StrCmpLogicalW() function in Windows, and call it within your IComparer's Compare function.

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]

private static extern int StrCmpLogicalW(string psz1, string psz2);

Just when using this function you should keep in mind that it’s behavior differs between different versions of windows S, so you need make sure that you check that carefully.

Here is an Example of how you can implement the whole thing within your code:

[SuppressUnmanagedCodeSecurity]
internal static class NativeMethods
{
    [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
    public static extern int StrCmpLogicalW(string psz1, string psz2);
}

To use it to Sort order Strings:

public sealed class SimpleStringComparer : IComparer<FileInfo>
{
    public int Compare(String a, String b)
    {
        return NativeMethods.StrCmpLogicalW(a, b);
    }
}

To use it the Sort FileInfo Object instances directly:

public sealed class FileInfoNameComparer : IComparer<FileInfo>
{
    public int Compare(FileInfo a, FileInfo b)
    {
        return NativeMethods.StrCmpLogicalW(a.Name, b.Name);
    }
}

I guess that should do!

2 Likes

One can even avoid the max query and use a large enough fixed value instead. It also works when the whole set is not at hand.