Natural Sort Order in C#

后端 未结 17 2077
野性不改
野性不改 2020-11-21 04:54

Anyone have a good resource or provide a sample of a natural order sort in C# for an FileInfo array? I am implementing the IComparer interface in

17条回答
  •  隐瞒了意图╮
    2020-11-21 05:50

    I've actually implemented it as an extension method on the StringComparer so that you could do for example:

    • StringComparer.CurrentCulture.WithNaturalSort() or
    • StringComparer.OrdinalIgnoreCase.WithNaturalSort().

    The resulting IComparer can be used in all places like OrderBy, OrderByDescending, ThenBy, ThenByDescending, SortedSet, etc. And you can still easily tweak case sensitivity, culture, etc.

    The implementation is fairly trivial and it should perform quite well even on large sequences.


    I've also published it as a tiny NuGet package, so you can just do:

    Install-Package NaturalSort.Extension
    

    The code including XML documentation comments and suite of tests is available in the NaturalSort.Extension GitHub repository.


    The entire code is this (if you cannot use C# 7 yet, just install the NuGet package):

    public static class StringComparerNaturalSortExtension
    {
        public static IComparer WithNaturalSort(this StringComparer stringComparer) => new NaturalSortComparer(stringComparer);
    
        private class NaturalSortComparer : IComparer
        {
            public NaturalSortComparer(StringComparer stringComparer)
            {
                _stringComparer = stringComparer;
            }
    
            private readonly StringComparer _stringComparer;
            private static readonly Regex NumberSequenceRegex = new Regex(@"(\d+)", RegexOptions.Compiled | RegexOptions.CultureInvariant);
            private static string[] Tokenize(string s) => s == null ? new string[] { } : NumberSequenceRegex.Split(s);
            private static ulong ParseNumberOrZero(string s) => ulong.TryParse(s, NumberStyles.None, CultureInfo.InvariantCulture, out var result) ? result : 0;
    
            public int Compare(string s1, string s2)
            {
                var tokens1 = Tokenize(s1);
                var tokens2 = Tokenize(s2);
    
                var zipCompare = tokens1.Zip(tokens2, TokenCompare).FirstOrDefault(x => x != 0);
                if (zipCompare != 0)
                    return zipCompare;
    
                var lengthCompare = tokens1.Length.CompareTo(tokens2.Length);
                return lengthCompare;
            }
            
            private int TokenCompare(string token1, string token2)
            {
                var number1 = ParseNumberOrZero(token1);
                var number2 = ParseNumberOrZero(token2);
    
                var numberCompare = number1.CompareTo(number2);
                if (numberCompare != 0)
                    return numberCompare;
    
                var stringCompare = _stringComparer.Compare(token1, token2);
                return stringCompare;
            }
        }
    }
    

提交回复
热议问题