Как сортировать целые строки?

У меня возникает странная проблема при сортировке списка строк с целыми значениями. Однако некоторые значения могут иметь префикс с некоторыми символами.

например.

// 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

Любое решение для этого в С#?

Ответ 1

Вы ищете алгоритм Alphanum. К счастью для вас, уже существует ряд реализаций. См. здесь.

Ответ 2

Вот как я решил это для нашего приложения, порядок будет как в каталоге 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

Ответ 3

То, что вы ищете, это Natural Sort.

Джефф Этвуд однажды сделал замечательную статью в своем блоге, объяснив концепцию и связавшись с другими источниками с помощью алгоритмов, которые вы могли бы взять в качестве примера.

Сортировка для людей: естественный порядок сортировки

Ответ 4

Здесь пользовательский сопоставитель, который будет сортироваться в требуемом порядке. Обратите внимание, что в этом коде нет проверок ошибок/здравомыслия: предполагается, что все строки будут в правильном формате.

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

Ответ 5

Ну, вы всегда можете запустить функцию API Win32 StrCmpLogicalW, которая делает именно то, что вы хотите (это то, что Explorer использует для сортировки имен файлов). Единственным возможным недостатком является то, что сортировка нечувствительна к регистру.

Ответ 6

Не уверен в производительности и уверен, что это можно оптимизировать, но он выполняет следующие действия:

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" });