Skip to content

Latest commit

 

History

History
333 lines (212 loc) · 9.5 KB

File metadata and controls

333 lines (212 loc) · 9.5 KB

IntRangeParser

IntRangeParser разбирает строку с целыми числами и диапазонами и возвращает отсортированный список уникальных значений.

Метод не бросает исключения для ошибок пользовательского ввода. Ошибки валидации возвращаются через Result<List<int>>.

Публичный API

Result<List<int>> IntRangeParser.Parse(string? rangeSource, int maxRangeValue);

Result<List<int>> IntRangeParser.Parse(
    string? rangeSource,
    int maxRangeValue,
    int maxResultCount);

Первая перегрузка сохраняет базовое поведение и ограничивает только допустимую верхнюю границу диапазона.

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

Пример для недоверенного ввода:

var result = IntRangeParser.Parse(
    rangeSource: userInput,
    maxRangeValue: 1_000_000,
    maxResultCount: 10_000);

Если результат содержит больше maxResultCount уникальных значений, метод возвращает Failure.

Формат входной строки

Поддерживаются токены следующих видов:

  • N — одиночное число;
  • N-M — диапазон от N до M;
  • -N — открытый слева диапазон;
  • N- — открытый справа диапазон.

Разделители токенов

Токены могут быть разделены любым символом из набора:

" ,.;_:#!|\\/'\""

Примеры:

  • 1,2,3
  • 1;2;3
  • 1 2 3
  • 1|2|3

Семантика

Обычное число N

Добавляется одно значение.

Примеры:

  • 5 -> [5]
  • 0 -> [0]

Обычный диапазон N-M

Добавляются все значения от меньшей границы к большей включительно.

Примеры:

  • 3-7 -> [3,4,5,6,7]
  • 7-3 -> [3,4,5,6,7]

Открытый слева диапазон -N

Интерпретируется как диапазон от 1 до N.

Примеры:

  • -5 -> [1,2,3,4,5]
  • -1 -> [1]

-0 считается ошибкой.

Открытый справа диапазон N-

Интерпретируется как диапазон от N до maxRangeValue.

Примеры:

  • 10-, maxRangeValue = 15 -> [10,11,12,13,14,15]
  • 0-, maxRangeValue = 5 -> [0,1,2,3,4,5]

Ноль и отрицательные значения

Ноль

Ноль допустим только при явном указании:

  • 0
  • 0-N
  • 0-

Примеры:

  • 0 -> [0]
  • 0-3 -> [0,1,2,3]
  • 0-, maxRangeValue = 3 -> [0,1,2,3]

Отрицательные значения

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

Примеры некорректного ввода:

  • 1--5
  • --5
  • -1-5

Важно: запись -N в начале строки — это не отрицательное число, а специальная форма открытого слева диапазона.

Ограничения

maxRangeValue

maxRangeValue должен быть не меньше 0.

Все явно указанные значения должны лежать в диапазоне:

0..maxRangeValue

Исключение: у формы -N левая граница неявная и всегда равна 1.

Примеры:

  • 5, maxRangeValue = 10 -> ok
  • 0-10, maxRangeValue = 10 -> ok
  • 11, maxRangeValue = 10 -> error
  • 5-12, maxRangeValue = 10 -> error
  • -12, maxRangeValue = 10 -> error

maxResultCount

maxResultCount доступен только во второй перегрузке и должен быть не меньше 0.

Лимит применяется к итоговому количеству уникальных значений. Если лимит превышен, метод возвращает Failure. Парсер старается отказывать как можно раньше: для одного диапазона лимит проверяется до материализации результата, а в потоковом сценарии — после обработки очередного токена.

Примеры:

IntRangeParser.Parse("1-100", maxRangeValue: 100, maxResultCount: 100); // ok
IntRangeParser.Parse("1-100", maxRangeValue: 100, maxResultCount: 99);  // Failure
IntRangeParser.Parse("", maxRangeValue: 100, maxResultCount: 0);        // ok, пустой список
IntRangeParser.Parse("0", maxRangeValue: 100, maxResultCount: 0);       // Failure

Позиционные правила

-N

Открытый слева диапазон допустим только первым токеном.

Примеры:

  • -5,10 -> ok
  • 10,-5 -> error

N-

Открытый справа диапазон допустим только последним токеном.

Примеры:

  • 5,10- -> ok
  • 10-,15 -> error

Пробелы вокруг -

Пробелы вокруг - внутри диапазона допустимы. Поддерживаются, например:

  • 10 - 20
  • 10- 20
  • 10 -20
  • - 20
  • 10 -

Перед разбором пробелы, непосредственно примыкающие к -, игнорируются.

Важно:

  • пробелы вокруг - не считаются разделителями токенов;
  • пробелы вне диапазонной конструкции остаются обычными разделителями.

Примеры:

  • 10 - 20 30 -> токены 10-20 и 30
  • 1 2 3 -> три отдельных токена

Дубликаты и порядок результата

Результат всегда:

  • без дубликатов;
  • отсортирован по возрастанию.

Примеры:

  • 1,2,2,3 -> [1,2,3]
  • 5-7,6,7,8 -> [5,6,7,8]

Ошибки

Метод возвращает Failure, если:

  • rangeSource == null;
  • maxRangeValue < 0;
  • maxResultCount < 0;
  • встречен некорректный токен;
  • в токене больше одного символа -;
  • токен невозможно распарсить как число или диапазон;
  • токен нарушает позиционные правила (-N не первый, N- не последний);
  • явная граница выходит за пределы 0..maxRangeValue;
  • обнаружена попытка задать отрицательное значение;
  • встречен -0;
  • результат содержит больше значений, чем разрешено параметром maxResultCount;
  • результат слишком велик для размещения в List<int>.

Примеры

Пример 1

var result = IntRangeParser.Parse("1,2,5-7", 10);

Результат:

[1,2,5,6,7]

Пример 2

var result = IntRangeParser.Parse("-5,10-", 12);

Результат:

[1,2,3,4,5,10,11,12]

Пример 3

var result = IntRangeParser.Parse("0-3, 10 - 12", 20);

Результат:

[0,1,2,3,10,11,12]

Пример 4

var result = IntRangeParser.Parse("7-3", 10);

Результат:

[3,4,5,6,7]

Пример 5

var result = IntRangeParser.Parse("10,-5", 20);

Результат:

Failure

Причина: -N допустим только первым токеном.

Пример 6

var result = IntRangeParser.Parse("-0", 20);

Результат:

Failure

Причина: открытый слева диапазон начинается с 1, -0 недопустим.

Пример 7

var result = IntRangeParser.Parse("1-1000", 10_000, maxResultCount: 100);

Результат:

Failure

Причина: результат содержит больше значений, чем разрешено параметром maxResultCount.

Замечания по реализации

Внутри парсер оптимизирован под int и сценарии:

  • небольшие и средние диапазоны;
  • большие плотные диапазоны;
  • большие, но разрежённые диапазоны.

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