Вот некоторый код, который использует регулярные выражения.
Обратите внимание, что вопрос, поднятый Бартом в комментариях к вашему вопросу, т.е. «Как вы справляетесь с 1 +++ 5», совсем не обрабатывается.
Чтобы это исправить, если ваш код уже не используется, и не подвержен изменению поведения, я бы предложил вам изменить свой синтаксис на следующий:
- используйте .. для обозначения диапазонов
- допускается как +, так и - для чисел, для положительных и отрицательных чисел
- используйте запятую и / или точку с запятой для разделения различных чисел или диапазонов
- разрешить пробел
Посмотрите на разницу между двумя следующими строками:
- 1 ++ 7 + 10 ++ 15 + 20 + 30 ++
- 1..7, 10..15, 20, 30 ..
Вторую строку гораздо легче разобрать и гораздо проще читать.
Это также устранит всю двусмысленность:
- 1 +++ 5 = 1 ++ + 5 = 1 .., 5
- 1 +++ 5 = 1 + ++ 5 = 1, .. 5
Нет способа неправильно разобрать второй синтаксис.
В любом случае, вот мой код. В основном это работает путем добавления четырех шаблонов регулярных выражений для четырех типов шаблонов:
- Num
- Num ++
- ++ Num
- Num ++ Num
Для «num» он будет обрабатывать отрицательные числа с начальным знаком минус и одной или несколькими цифрами. По понятным причинам он не обрабатывает знак плюс как часть числа.
Я интерпретировал "и выше", чтобы обозначить "до Int32.MaxValue" и то же самое для вплоть до Int32.MinValue.
public class Range
{
public readonly Int32 From;
public readonly Int32 To;
public Range(Int32 from, Int32 to)
{
From = from;
To = to;
}
public override string ToString()
{
if (From == To)
return From.ToString();
else if (From == Int32.MinValue)
return String.Format("++{0}", To);
else if (To == Int32.MaxValue)
return String.Format("{0}++", From);
else
return String.Format("{0}++{1}", From, To);
}
}
public static class RangeSplitter
{
public static Range[] Split(String s)
{
if (s == null)
throw new ArgumentNullException("s");
String[] parts = new Regex(@"(?<!\+)\+(?!\+)").Split(s);
List<Range> result = new List<Range>();
var patterns = new Dictionary<Regex, Action<Int32[]>>();
patterns.Add(new Regex(@"^(-?\d+)$"),
values => result.Add(new Range(values[0], values[0])));
patterns.Add(new Regex(@"^(-?\d+)\+\+$"),
values => result.Add(new Range(values[0], Int32.MaxValue)));
patterns.Add(new Regex(@"^\+\+(-?\d+)$"),
values => result.Add(new Range(Int32.MinValue, values[0])));
patterns.Add(new Regex(@"^(-?\d+)\+\+(-?\d+)$"),
values => result.Add(new Range(values[0], values[1])));
foreach (String part in parts)
{
foreach (var kvp in patterns)
{
Match ma = kvp.Key.Match(part);
if (ma.Success)
{
Int32[] values = ma.Groups
.OfType<Group>()
.Skip(1) // group 0 is the entire match
.Select(g => Int32.Parse(g.Value))
.ToArray();
kvp.Value(values);
}
}
}
return result.ToArray();
}
}
Юнит-тесты:
[TestFixture]
public class RangeSplitterTests
{
[Test]
public void Split_NullString_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() =>
{
var result = RangeSplitter.Split(null);
});
}
[Test]
public void Split_EmptyString_ReturnsEmptyArray()
{
Range[] result = RangeSplitter.Split(String.Empty);
Assert.That(result.Length, Is.EqualTo(0));
}
[TestCase(01, "++7", Int32.MinValue, 7)]
[TestCase(02, "7", 7, 7)]
[TestCase(03, "7++", 7, Int32.MaxValue)]
[TestCase(04, "1++7", 1, 7)]
public void Split_SinglePatterns_ProducesExpectedRangeBounds(
Int32 testIndex, String input, Int32 expectedLower,
Int32 expectedUpper)
{
Range[] result = RangeSplitter.Split(input);
Assert.That(result.Length, Is.EqualTo(1));
Assert.That(result[0].From, Is.EqualTo(expectedLower));
Assert.That(result[0].To, Is.EqualTo(expectedUpper));
}
[TestCase(01, "++7")]
[TestCase(02, "7++")]
[TestCase(03, "1++7")]
[TestCase(04, "1+7")]
[TestCase(05, "1++7+10++15+20+30++")]
public void Split_ExamplesFromQuestion_ProducesCorrectResults(
Int32 testIndex, String input)
{
Range[] ranges = RangeSplitter.Split(input);
String rangesAsString = String.Join("+",
ranges.Select(r => r.ToString()).ToArray());
Assert.That(rangesAsString, Is.EqualTo(input));
}
[TestCase(01, 10, 10, "10")]
[TestCase(02, 1, 10, "1++10")]
[TestCase(03, Int32.MinValue, 10, "++10")]
[TestCase(04, 10, Int32.MaxValue, "10++")]
public void RangeToString_Patterns_ProducesCorrectResults(
Int32 testIndex, Int32 lower, Int32 upper, String expected)
{
Range range = new Range(lower, upper);
Assert.That(range.ToString(), Is.EqualTo(expected));
}
}