Как отсортировать целочисленные строки? - PullRequest
11 голосов
/ 24 апреля 2009

Я столкнулся со странной проблемой при сортировке списка строк с целочисленными значениями. Однако некоторым значениям может предшествовать несколько символов.

, например

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

Есть ли какое-нибудь решение для этого в C #?

Ответы [ 6 ]

17 голосов
/ 24 апреля 2009

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

5 голосов
/ 24 апреля 2009

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

3 голосов
/ 24 апреля 2009

То, что вы ищете, является естественной сортировкой.

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

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

1 голос
/ 24 апреля 2009

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

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
0 голосов
/ 24 апреля 2009

Не уверен насчет производительности, и уверен, что это можно оптимизировать, но это делает работу:

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" });
0 голосов
/ 24 апреля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...