Ответ 1
Вы ищете алгоритм Alphanum. К счастью для вас, уже существует ряд реализаций. См. здесь.
У меня возникает странная проблема при сортировке списка строк с целыми значениями. Однако некоторые значения могут иметь префикс с некоторыми символами.
например.
// B1, 5, 50, A10, 7, 72, B3, A1, A2
В основном есть номера страниц и их следует сортировать следующим образом:
// A1, A2, A10, B1, B3, 5, 7, 50, 72
Но если я использую сортировку по умолчанию по умолчанию, они будут отсортированы как
// A1, A10, A2, B1, B3, 5, 50, 7, 72
Любое решение для этого в С#?
Вы ищете алгоритм Alphanum. К счастью для вас, уже существует ряд реализаций. См. здесь.
Вот как я решил это для нашего приложения, порядок будет как в каталоге Windows:
public class NaturalSortComparer : IComparer<string>
{
public int Compare(string x, string y)
{
return StrCmpLogicalW(x, y);
}
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
public static extern int StrCmpLogicalW(string x, string y);
}
Использование:
NaturalSortComparer comparer = new NaturalSortComparer();
return comparer.Compare(string1, string2);
Но это, вероятно, не совсем то, что вы хотите:
//A1, A2, A10, B1, B3, 5, 7, 50, 72
Это даст
//5, 7, 50, 72, A1, A2, A10, B1, B3
То, что вы ищете, это Natural Sort.
Джефф Этвуд однажды сделал замечательную статью в своем блоге, объяснив концепцию и связавшись с другими источниками с помощью алгоритмов, которые вы могли бы взять в качестве примера.Здесь пользовательский сопоставитель, который будет сортироваться в требуемом порядке. Обратите внимание, что в этом коде нет проверок ошибок/здравомыслия: предполагается, что все строки будут в правильном формате.
public class MyComparer : IComparer<string>
{
public int Compare(string x, string y)
{
Match xMatch = Regex.Match(x, @"^(\D*)(\d+)$");
Match yMatch = Regex.Match(y, @"^(\D*)(\d+)$");
string xChars = xMatch.Groups[1].Value;
string yChars = yMatch.Groups[1].Value;
if ((xChars.Length == 0) && (yChars.Length > 0))
{
return 1;
}
else if ((xChars.Length > 0) && (yChars.Length == 0))
{
return -1;
}
else
{
int charsResult = xChars.CompareTo(yChars);
return (charsResult != 0)
? charsResult
: int.Parse(xMatch.Groups[2].Value)
.CompareTo(int.Parse(yMatch.Groups[2].Value));
}
}
}
Вы можете использовать его так:
List<string> testList =
new List<string>() { "B1","5","50","A10","7","72","B3","A1","A2" };
testList.Sort(new MyComparer()); // A1, A2, A10, B1, B3, 5, 7, 50, 72
Ну, вы всегда можете запустить функцию API Win32 StrCmpLogicalW
, которая делает именно то, что вы хотите (это то, что Explorer использует для сортировки имен файлов). Единственным возможным недостатком является то, что сортировка нечувствительна к регистру.
Не уверен в производительности и уверен, что это можно оптимизировать, но он выполняет следующие действия:
string[] sort(string[] data)
{
return data
.OrderBy(s => Regex.Match(s, @"^\D").Length == 0)
.ThenBy(s => Regex.Match(s, @"\D*").Value)
.ThenBy(s => Int32.Parse(Regex.Match(s, @"\d+").Value)).ToArray();
}
var result = sort(new string[] { "B1", "5", "50", "A10", "7", "72", "B3", "A1", "A2" });