C # - Как сортировать строки, содержащие текст и цифры - PullRequest
0 голосов
/ 10 ноября 2018

У меня есть просмотр списка, который заполнен компьютерными именами и именами пользователей и числом (в виде строки). Я уже создал свой собственный ListViewItemComparer, используя интерфейс IComparer. Но он не сортирует элементы правильно, как я хочу. Вот как это должно сортировать компьютеры, например: Компьютер-1 Компьютер-2 Компьютер-3 ... Компьютер-15

и вот как они сортируются: Компьютер-1 Компьютер-10 Компьютер-11 ... Компьютер-2 Компьютер-3

Проблема в том, что я не могу просто вырезать «компьютерную» часть и сравнить числа, которые следуют, поскольку это был просто пример, а имена компьютеров могут быть чем угодно (aaa393bbb333, ccccvvvv, 222hhhdh, Computer-01, Computer -02, ....)

Это мой код:

 private bool isNumeric(String pInput)
    {
        int o;
        return int.TryParse(pInput, out o);
    }

    public int Compare(object x, object y)
    {
        ListViewItem itemX = x as ListViewItem;
        ListViewItem itemY = y as ListViewItem;
        //
        int returnVal = -1;
        if (itemX == null && itemY == null) returnVal = 0;
        else if (itemX == null) returnVal = -1;
        else if (itemY == null) returnVal = 1;
        else if (itemX.SubItems.Count - 1 < col && itemY.SubItems.Count - 1 < col) returnVal = 0;
        else if (itemX.SubItems.Count - 1 < col) returnVal = -1;
        else if (itemY.SubItems.Count - 1 < col) returnVal = 1;
        else if(isNumeric(itemX.SubItems[col].Text) && isNumeric(itemY.SubItems[col].Text))
        {
            //used for number comparison
            int value1 = int.Parse(itemX.SubItems[col].Text);
            int value2 = int.Parse(itemY.SubItems[col].Text);
            if (value1 == value2) returnVal = 0;
            else if (value1 < value2) returnVal = -1;
            else if (value1 > value2) returnVal = 1;
        }
        else returnVal = String.Compare(itemX.SubItems[col].Text, itemY.SubItems[col].Text);
        if (order == SortOrder.Descending)
            returnVal *= -1;
        return returnVal;
    }

1 Ответ

0 голосов
/ 10 ноября 2018

Вы можете использовать выражение регулярного выражения

(?<=\D)(?=\d)|(?<=\d)(?=\D)

, чтобы разбить строку на числа и текст.

string[] parts = Regex.Split(theString, @"(?<=\D)(?=\d)|(?<=\d)(?=\D)");

Это дает поочередно цифры и тексты. Как это работает:

(?<=exp)pos      Match any position pos following a prefix exp. 
pos(?=exp)       Match any position pos preceding a suffix exp. 

Таким образом, регулярное выражение означает разделение в позиции между текстом и числом или в позиции между числом и текстом, где \D обозначает нецифровый символ, \d обозначает цифру и | обозначает ИЛИ.

Вам нужно проверить, является ли первая часть текстом или числом с помощью

bool firstIsNumber = Char.IsDigit(parts[0][0]);

Если первая часть является числом, все части в четных индексах являются числами, а все части в нечетных индексах являются текстами и наоборот.

private static readonly Regex numTextSplitRegex =
    new Regex(@"(?<=\D)(?=\d)|(?<=\d)(?=\D)", RegexOptions.Compiled);

public int Compare(string x, string y)
{
    x = x ?? "";
    y = y ?? "";
    string[] xParts = numTextSplitRegex.Split(x);
    string[] yParts = numTextSplitRegex.Split(y);

    bool firstXIsNumber = xParts[0].Length > 0 && Char.IsDigit(xParts[0][0]);
    bool firstYIsNumber = yParts[0].Length > 0 && Char.IsDigit(yParts[0][0]);

    if (firstXIsNumber != firstYIsNumber) {
        return x.CompareTo(y);
    }

    for (int i = 0; i < Math.Min(xParts.Length, yParts.Length); i++) {
        int result;
        if (firstXIsNumber == (i % 2 == 0)) { // Compare numbers.
            long a = Int64.Parse(xParts[i]);
            long b = Int64.Parse(yParts[i]);
            result = a.CompareTo(b);
        } else { // Compare texts.
            result = xParts[i].CompareTo(yParts[i]);
        }
        if (result != 0) {
            return result;
        }
    }
    return xParts.Length.CompareTo(yParts.Length);
}
...