C#: Natural sorting

Published:

When you create an application that displays data in lists or tables, you often run into the problem of sorting. When dealing with only numbers it isn't a big deal, but when sorting text it can be. Regular sorting is often done by alphanumerically, which means that "bear" comes before "cat" and "5" comes before "7". The problem is that this is done letter by letter, which works most of the time, except when you get numbers mixed in with your text. Then you end up with for example "2" coming after "10", since "10" starts with a "1" which comes before "2". The solution to this is something called natural sorting.

I won't write a lot about that here, but just say that it tries to sort things the way humans do. Anyways, below you find a C# class that handles this for you. I put it together by looking around and taking some bits and pieces from here and there, so I can't really take credit for it. I only post it here so that I won't lose it, cause it was really helpful.

The class uses some built-in sorting functions in windows and implements the IComparer interface. It can for example be used with the OrderBy extension methods and the Sort methods of List.

using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;

namespace Geekality
{
    public sealed class NaturalStringComparer : IComparer<string>
    {
        private readonly int modifier = 1;

        public NaturalStringComparer(bool descending)
        {
            if (descending)
                modifier = -1;
        }

        public NaturalStringComparer()
            :this(false) {}

        public int Compare(string a, string b)
        {
            return SafeNativeMethods.StrCmpLogicalW(a ?? "", b ?? "") * modifier;
        }
    }

    public sealed class NaturalFileInfoComparer : IComparer<FileInfo>
    {
        public int Compare(FileInfo a, FileInfo b)
        {
            return SafeNativeMethods.StrCmpLogicalW(a.Name ?? "", b.Name ?? "");
        }
    }

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