Вы можете использовать выражение регулярного выражения
(?<=\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);
}