[C#] Natural string comparison
От: Andy77 Ниоткуда  
Дата: 12.04.11 23:19
Оценка: 36 (3)
Погуглил немного готовые реализации — ужас какие неэффективные! Пришлось набросать свою, там всего-то пара строк кода, но вдруг кому-то пригодится.

[TestFixture]
public class NaturalComparerTest
{
   readonly NaturalComparer cmp = new NaturalComparer();

   [Test]
   [TestCase(null, "")]
   [TestCase("", "a")]
   [TestCase("a", "aa")]
   [TestCase("3", "4")]
   [TestCase("3", "34")]
   [TestCase("3", "a")]
   [TestCase("a3", "a4")]
   [TestCase("a3", "b3")]
   [TestCase("a34", "a34b")]
   [TestCase("AK47", "AK74")]
   [TestCase("I-9", "I-10")]
   [TestCase("5-out-of-8", "5-out-of-20")]
   [TestCase("5-out-of-8-apples", "5-out-of-20-apples")]
   [TestCase("episode-1-part-1", "episode-1-part-2")]
   public void TestLess(string x, string y)
   {
      Assert.IsTrue(cmp.Compare(x, y) < 0);
   }

   [Test]
   [TestCase(null, null)]
   [TestCase("", "")]
   [TestCase("a", "a")]
   [TestCase("4", "4")]
   [TestCase("43", "43")]
   [TestCase("a-43", "a-43")]
   [TestCase("43-a-34", "43-a-34")]
   public void TestEqual(string x, string y)
   {
      Assert.AreEqual(0, cmp.Compare(x, y));
   }
}


public class NaturalComparer : IComparer<string>
{
   public int Compare(string x, string y)
   {
      if (x == null && y == null) return 0;
      if (x == null) return -1;
      if (y == null) return 1;

      int lx = x.Length, ly = y.Length;

      for (int mx = 0, my = 0; mx < lx && my < ly; mx++, my++)
      {
         if (char.IsDigit(x[mx]) && char.IsDigit(y[my]))
         {
            long vx = 0, vy = 0;

            for (; mx < lx && char.IsDigit(x[mx]); mx++)
               vx = vx * 10 + x[mx] - '0';

            for (; my < ly && char.IsDigit(y[my]); my++)
               vy = vy * 10 + y[my] - '0';

            if (vx != vy)
               return vx > vy ? 1 : -1;
         }

         if (mx < lx && my < ly && x[mx] != y[my])
            return x[mx] > y[my] ? 1 : -1;
      }

      return lx - ly;
   }
}
Re: [C#] Natural string comparison
От: Kalina9001  
Дата: 18.04.11 09:22
Оценка: 10 (1)
Здравствуйте, Andy77, Вы писали:

Потестировал:
var forSort = new List<string>(
                    new[]
                        {
                            "      b3.txt",
                            "10",
                            "x10m.txt",
                            "20",
                            "2",
                            "444444",
                            "b10.txt",
                            "a10b1.txt",
                            "10.txt",
                            "3.txt", 
                            "x2m.txt",
                            "a1b1.txt",
                            "a2b2.txt", 
                            "        ",                            
                            "01",
                            "a2b1.txt", 
                            "a2b11.txt", 
                            "     b4.txt    ",
                            "b1.txt", 
                            "1.txt",
                            "0000010.txt",
                            "b2.txt",
                            "1",
                            "",
                            " 15",    
                            "                         16",  
                        });

            forSort.Sort(new NaturalComparer());

            var expected = new List<string>(
                    new[]
                        {
                            "",
                            "        ",   
                            "01",
                            "1",
                            "1.txt", 
                            "2",
                            "3.txt", 
                            "0000010.txt",
                            "10",
                            "10.txt",
                            " 15", 
                            "                         16",  
                            "20",
                            "444444",
                            "a1b1.txt", 
                            "a2b1.txt", 
                            "a2b2.txt", 
                            "a2b11.txt", 
                            "a10b1.txt", 
                            "b1.txt", 
                            "b2.txt",
                            "      b3.txt",
                            "     b4.txt    ",
                            "b10.txt",
                            "x2m.txt",
                            "x10m.txt",
                        });


После сортировки ожидал в массиве forSort данные равные expected
Или я "Natural string comparison" не правильно понял?
... << RSDN@Home 1.2.0 alpha 5 rev. 1497>>
Re[2]: [C#] Natural string comparison
От: Andy77 Ниоткуда  
Дата: 23.04.11 23:11
Оценка: 9 (1)
K>Потестировал:
K>После сортировки ожидал в массиве forSort данные равные expected

Спасибо! Действительно, про пробелы я забыл. Вот улучшенная версия:

public class NaturalComparer : IComparer<string>
{
   public int Compare(string x, string y)
   {
      if (x == null && y == null) return 0;
      if (x == null) return -1;
      if (y == null) return 1;

      int lx = x.Length, ly = y.Length;
      int nwx = lx, nwy = ly;

      for (int mx = 0, my = 0; mx < lx && my < ly; mx++, my++)
      {
         if (char.IsDigit(x[mx]) && char.IsDigit(y[my]))
         {
            long vx = 0, vy = 0;

            for (; mx < lx && char.IsDigit(x[mx]); mx++)
               vx = vx * 10 + x[mx] - '0';

            for (; my < ly && char.IsDigit(y[my]); my++)
               vy = vy * 10 + y[my] - '0';

            if (vx != vy)
               return vx > vy ? 1 : -1;
         }

         for (; mx < lx && char.IsWhiteSpace(x[mx]); mx++, nwx--) ;
         for (; my < ly && char.IsWhiteSpace(y[my]); my++, nwy--) ;

         if (mx < lx && my < ly && x[mx] != y[my])
            return x[mx] > y[my] ? 1 : -1;
      }

      return nwx - nwy;
   }
}
Re: [C#] Natural string comparison
От: Alexey Axyonov Украина  
Дата: 17.05.11 11:29
Оценка:
Здравствуйте, Andy77, Вы писали:

A>Погуглил немного готовые реализации — ужас какие неэффективные! Пришлось набросать свою, там всего-то пара строк кода, но вдруг кому-то пригодится.


А как насчет:

        public sealed class NaturalStringComparer : IComparer<string>
        {
            private const string Shlwapi = "shlwapi.dll";

            #region IComparer<string> Members

            public int Compare(string x, string y)
            {
                return StrCmpLogicalW(x, y);
            }

            #endregion

            [DllImport(Shlwapi, CharSet = CharSet.Unicode)]
            private static extern int StrCmpLogicalW(
                [MarshalAs(UnmanagedType.LPWStr)] string psz1,
                [MarshalAs(UnmanagedType.LPWStr)] string psz2);
        }


?
... << RSDN@Home 1.2.0 alpha 5 rev. 1510>>
Re[2]: [C#] Natural string comparison
От: Andy77 Ниоткуда  
Дата: 21.06.11 03:18
Оценка:
Здравствуйте, Alexey Axyonov, Вы писали:

AA>А как насчет:


Тоже сработает, наверное. Мне было важно получить код на C# безо всяких зависимостей.
Re[2]: [C#] Natural string comparison
От: _FRED_ Черногория
Дата: 01.07.11 16:59
Оценка:
Здравствуйте, Alexey Axyonov, Вы писали:

A>>Погуглил немного готовые реализации — ужас какие неэффективные! Пришлось набросать свою, там всего-то пара строк кода, но вдруг кому-то пригодится.


AA>А как насчет:


AA>            [DllImport(Shlwapi, CharSet = CharSet.Unicode)]
AA>            private static extern int StrCmpLogicalW(
AA>                [MarshalAs(UnmanagedType.LPWStr)] string psz1,
AA>                [MarshalAs(UnmanagedType.LPWStr)] string psz2);
AA>        }

AA>?

Переключение между управляемым кодом и неуправляемым (и обратно) сильно затратное по времени.
Help will always be given at Hogwarts to those who ask for it.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.