У меня есть список, который заполнен именами компьютеров, именами пользователей и числом (в виде строки). Я уже создал свой собственный 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;
}
🤔 А знаете ли вы, что...
C# поддерживает LINQ (Language Integrated Query) для удобного запроса и обработки данных.
Вы можете использовать выражение регулярного выражения
(?<=\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
- цифровой символ и |
- OR.
Вам нужно будет проверить, является ли первая часть текстом или числом с
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);
}