Natural Sort Order in C#

后端 未结 17 2081
野性不改
野性不改 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:38

    Here's a relatively simple example that doesn't use P/Invoke and avoids any allocation during execution.

    internal sealed class NumericStringComparer : IComparer
    {
        public static NumericStringComparer Instance { get; } = new NumericStringComparer();
    
        public int Compare(string x, string y)
        {
            // sort nulls to the start
            if (x == null)
                return y == null ? 0 : -1;
            if (y == null)
                return 1;
    
            var ix = 0;
            var iy = 0;
    
            while (true)
            {
                // sort shorter strings to the start
                if (ix >= x.Length)
                    return iy >= y.Length ? 0 : -1;
                if (iy >= y.Length)
                    return 1;
    
                var cx = x[ix];
                var cy = y[iy];
    
                int result;
                if (char.IsDigit(cx) && char.IsDigit(cy))
                    result = CompareInteger(x, y, ref ix, ref iy);
                else
                    result = cx.CompareTo(y[iy]);
    
                if (result != 0)
                    return result;
    
                ix++;
                iy++;
            }
        }
    
        private static int CompareInteger(string x, string y, ref int ix, ref int iy)
        {
            var lx = GetNumLength(x, ix);
            var ly = GetNumLength(y, iy);
    
            // shorter number first (note, doesn't handle leading zeroes)
            if (lx != ly)
                return lx.CompareTo(ly);
    
            for (var i = 0; i < lx; i++)
            {
                var result = x[ix++].CompareTo(y[iy++]);
                if (result != 0)
                    return result;
            }
    
            return 0;
        }
    
        private static int GetNumLength(string s, int i)
        {
            var length = 0;
            while (i < s.Length && char.IsDigit(s[i++]))
                length++;
            return length;
        }
    }
    

    It doesn't ignore leading zeroes, so 01 comes after 2.

    Corresponding unit test:

    public class NumericStringComparerTests
    {
        [Fact]
        public void OrdersCorrectly()
        {
            AssertEqual("", "");
            AssertEqual(null, null);
            AssertEqual("Hello", "Hello");
            AssertEqual("Hello123", "Hello123");
            AssertEqual("123", "123");
            AssertEqual("123Hello", "123Hello");
    
            AssertOrdered("", "Hello");
            AssertOrdered(null, "Hello");
            AssertOrdered("Hello", "Hello1");
            AssertOrdered("Hello123", "Hello124");
            AssertOrdered("Hello123", "Hello133");
            AssertOrdered("Hello123", "Hello223");
            AssertOrdered("123", "124");
            AssertOrdered("123", "133");
            AssertOrdered("123", "223");
            AssertOrdered("123", "1234");
            AssertOrdered("123", "2345");
            AssertOrdered("0", "1");
            AssertOrdered("123Hello", "124Hello");
            AssertOrdered("123Hello", "133Hello");
            AssertOrdered("123Hello", "223Hello");
            AssertOrdered("123Hello", "1234Hello");
        }
    
        private static void AssertEqual(string x, string y)
        {
            Assert.Equal(0, NumericStringComparer.Instance.Compare(x, y));
            Assert.Equal(0, NumericStringComparer.Instance.Compare(y, x));
        }
    
        private static void AssertOrdered(string x, string y)
        {
            Assert.Equal(-1, NumericStringComparer.Instance.Compare(x, y));
            Assert.Equal( 1, NumericStringComparer.Instance.Compare(y, x));
        }
    }
    

提交回复
热议问题