Погуглил немного готовые реализации — ужас какие неэффективные! Пришлось набросать свою, там всего-то пара строк кода, но вдруг кому-то пригодится.
[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;
}
}
Здравствуйте, 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>>
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;
}
}
Здравствуйте, 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>>
Здравствуйте, 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>?
Переключение между управляемым кодом и неуправляемым (и обратно) сильно затратное по времени.