Ti-Soft.StringFunctions 2.7.0

dotnet add package Ti-Soft.StringFunctions --version 2.7.0
                    
NuGet\Install-Package Ti-Soft.StringFunctions -Version 2.7.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Ti-Soft.StringFunctions" Version="2.7.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Ti-Soft.StringFunctions" Version="2.7.0" />
                    
Directory.Packages.props
<PackageReference Include="Ti-Soft.StringFunctions" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Ti-Soft.StringFunctions --version 2.7.0
                    
#r "nuget: Ti-Soft.StringFunctions, 2.7.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Ti-Soft.StringFunctions@2.7.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Ti-Soft.StringFunctions&version=2.7.0
                    
Install as a Cake Addin
#tool nuget:?package=Ti-Soft.StringFunctions&version=2.7.0
                    
Install as a Cake Tool

StringFunctions

StringFunctions — небольшая библиотека с утилитами для работы со строками.

Сейчас библиотека включает двенадцать основных блоков:

  • проверка баланса скобок и кавычек;
  • нормализация строк;
  • разбор строковых диапазонов целых чисел в коллекцию;
  • форматирование коллекции целых чисел обратно в строку диапазонов;
  • согласование существительных с числом в русском языке;
  • перевод целых чисел в запись словами (прописью) на русском языке;
  • запись денежной суммы прописью на русском языке;
  • порядковые числительные словами на русском языке;
  • запись даты прописью на русском языке;
  • разбор количественного числительного из слов обратно в число;
  • запись времени прописью («цифровой» формат) на русском языке;
  • запись времени прописью (разговорный формат) на русском языке.

Библиотека ориентирована на практическое использование в прикладных проектах и делает упор на:

  • предсказуемый контракт;
  • отсутствие исключений для ошибок пользовательского ввода;
  • возврат результатов через ResultType;
  • поддержку нескольких версий .NET;
  • покрытие тестами.

Поддерживаемые платформы

Библиотека мультитаргетится на:

  • net6.0
  • net7.0
  • net8.0
  • net9.0
  • net10.0

Несмотря на то что net6.0 и net7.0 уже не поддерживаются Microsoft, они сохранены ради совместимости с существующими проектами.


Зависимости

Публичный API библиотеки использует ResultType.

Это означает, что методы, которые могут завершиться ошибкой пользовательского ввода или валидации, возвращают:

  • Result<T> — если нужно вернуть значение;
  • Result — если нужно вернуть только факт успеха или текст ошибки.

Такой подход позволяет не использовать исключения для штатных ошибок входных данных.


Возможности библиотеки

1. Проверка баланса скобок и кавычек

Проверка выполняется методом IsBracesBalanced.

Поддерживаются:

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

Метод возвращает:

  • признак сбалансированности;
  • символ, на котором баланс нарушается.

Пример:

using StringFunctions;
using StringFunctions.Braces;

string source = "(a + b) * [c]";

var result = source.IsBracesBalanced(KnownBracesTypes.CommonBraces);

if (result.IsSuccess)
{
    var (isBalanced, unbalancedSymbol) = result.Value;
    Console.WriteLine(isBalanced);       // True
    Console.WriteLine(unbalancedSymbol); // '\0'
}
else
{
    Console.WriteLine(result.Error);
}

Пример с ошибкой:

using StringFunctions;
using StringFunctions.Braces;

string source = "(a + b]";

var result = source.IsBracesBalanced(KnownBracesTypes.CommonBraces);

if (result.IsSuccess)
{
    var (isBalanced, unbalancedSymbol) = result.Value;
    Console.WriteLine(isBalanced);       // False
    Console.WriteLine(unbalancedSymbol); // '('
}

2. Нормализация строк

Нормализация выполняется методом NormalizeString.

Метод приводит строку к более чистому виду по внутренним правилам библиотеки, убирая:

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

Пример:

using StringFunctions;

string source = "  Привет   ,   мир !  ";

var result = source.NormalizeString();

if (result.IsSuccess)
{
    Console.WriteLine(result.Value); // "Привет, мир!"
}
else
{
    Console.WriteLine(result.Error);
}

Для null возвращается Failure, а для пустой или состоящей только из пробелов строки — Success(string.Empty).


3. Разбор строковых диапазонов целых чисел

Разбор выполняется методом IntRangeParser.Parse.

Поддерживаются формы записи:

  • N
  • N-M
  • -N
  • N-

Дополнительно поддерживаются:

  • явный 0;
  • 0-N;
  • 0-;
  • пробелы вокруг - внутри диапазона.

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

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

Пример:

using StringFunctions;

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

if (result.IsSuccess)
{
    // [1, 3, 4, 5, 8, 10, 11, 12]
    Console.WriteLine(string.Join(", ", result.Value));
}
else
{
    Console.WriteLine(result.Error);
}

Пример с открытыми диапазонами:

using StringFunctions;

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

if (result.IsSuccess)
{
    // [1, 2, 3, 4, 5, 10, 11, 12]
    Console.WriteLine(string.Join(", ", result.Value));
}

Пример с нулём:

using StringFunctions;

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

if (result.IsSuccess)
{
    // [0, 1, 2, 3, 10, 11, 12]
    Console.WriteLine(string.Join(", ", result.Value));
}

Полная спецификация: docs/int-range-parser.md


4. Форматирование коллекции чисел в строку диапазонов

Форматирование выполняется методом IntRangeFormatter.Format.

Библиотека поддерживает две перегрузки:

  • базовую — без знания maxRangeValue;
  • расширенную — с maxRangeValue и возможностью использовать открытые диапазоны.

Formatter:

  • принимает IEnumerable<int>;
  • сортирует входные значения;
  • удаляет дубли;
  • склеивает соседние значения в диапазоны.

Пример базовой перегрузки:

using StringFunctions;

var result = IntRangeFormatter.Format(new[] { 7, 3, 2, 1, 3, 8, 9, 5 }, ", ");

if (result.IsSuccess)
{
    Console.WriteLine(result.Value); // "1-3, 5, 7-9"
}

Пример с использованием открытых диапазонов:

using StringFunctions;

var result = IntRangeFormatter.Format(new[] { 1, 2, 3, 4, 5 }, 10);

if (result.IsSuccess)
{
    Console.WriteLine(result.Value); // "-5"
}

Пример с явным нулём:

using StringFunctions;

var result = IntRangeFormatter.Format(new[] { 0, 1, 2, 3 }, 3);

if (result.IsSuccess)
{
    Console.WriteLine(result.Value); // "0-"
}

Полная спецификация: docs/int-range-formatter.md


5. Согласование существительных с числом (русский язык)

Русский язык требует разной формы существительного при счёте: «1 яблоко», «2 яблока», «5 яблок». Класс RussianPlural (пространство имён StringFunctions.Russian) помогает подобрать правильную форму.

  • GetForm — определяет грамматическую форму (One / Few / Many) и не может завершиться ошибкой;
  • Pluralize — возвращает подходящую форму слова;
  • Quantify — собирает строку вида «5 яблок».

Знак числа не влияет на результат.

Пример:

using StringFunctions.Russian;

var word = RussianPlural.Pluralize(5, "яблоко", "яблока", "яблок");
Console.WriteLine(word.Value); // "яблок"

var phrase = RussianPlural.Quantify(2, "файл", "файла", "файлов");
Console.WriteLine(phrase.Value); // "2 файла"

RussianPluralForm form = RussianPlural.GetForm(21);
Console.WriteLine(form); // One

6. Число прописью (русский язык)

Класс RussianNumberToWords (пространство имён StringFunctions.Russian) переводит целые числа в запись словами в именительном падеже.

  • учитывается грамматический род единиц (один/одна/одно, два/две);
  • разрядные слова согласуются с числом (тысяча/тысячи/тысяч, миллион/миллиона/миллионов);
  • поддерживается склонение по всем 6 падежам (RussianCase);
  • поддерживается весь диапазон long, включая отрицательные значения и long.MinValue.

Перевод поддерживает склонение по падежам через перегрузку с RussianCase, а винительный падеж формируется для неодушевлённого счёта.

Пример:

using StringFunctions.Russian;

Console.WriteLine(RussianNumberToWords.Convert(1_234_567));
// "один миллион двести тридцать четыре тысячи пятьсот шестьдесят семь"

Console.WriteLine(RussianNumberToWords.Convert(2000, RussianGender.Feminine));
// "две тысячи"

Console.WriteLine(RussianNumberToWords.Convert(523, RussianCase.Genitive));
// "пятисот двадцати трёх"

Console.WriteLine(RussianNumberToWords.Convert(-5));
// "минус пять"

Эргономичные методы расширения

Для чисел int и long доступны методы расширения (пространство имён StringFunctions.Russian):

using StringFunctions.Russian;

Console.WriteLine(523.ToRussianWords(RussianCase.Genitive)); // "пятисот двадцати трёх"
Console.WriteLine(5.Pluralize("яблоко", "яблока", "яблок").Value); // "яблок"
Console.WriteLine(2.Quantify("файл", "файла", "файлов").Value); // "2 файла"
RussianPluralForm form = 5.GetRussianPluralForm(); // Many

7. Сумма (валюта) прописью (русский язык)

Класс RussianMoneyToWords (пространство имён StringFunctions.Russian) записывает денежную сумму прописью.

  • основная часть всегда прописью и согласуется с валютой (род, счётная форма);
  • разменная часть — прописью или цифрами (RussianMinorFormat);
  • валюта настраивается через RussianCurrency; готовые варианты: Rubles, Dollars, Euros;
  • поддерживаются перегрузки по (long major, int minor) и по decimal.

Пример:

using StringFunctions.Russian;

Console.WriteLine(RussianMoneyToWords.Convert(123, 45, RussianCurrency.Rubles).Value);
// "сто двадцать три рубля сорок пять копеек"

Console.WriteLine(RussianMoneyToWords.Convert(100, 5, RussianCurrency.Rubles, RussianMinorFormat.Digits).Value);
// "сто рублей 05 коп."

Console.WriteLine(123.45m.ToRussianMoney().Value);
// "сто двадцать три рубля сорок пять копеек"

Своя валюта задаётся через RussianCurrency и RussianNoun:

var tenge = new RussianCurrency(
    new RussianNoun("тенге", "тенге", "тенге", RussianGender.Masculine),
    new RussianNoun("тиын", "тиына", "тиынов", RussianGender.Masculine),
    "тиын");

8. Порядковые числительные (русский язык)

Класс RussianOrdinalToWords (пространство имён StringFunctions.Russian) переводит число в порядковое числительное с учётом рода и падежа.

  • в составном числительном склоняется и принимает род только последнее слово;
  • поддерживаются все роды и падежи (RussianGender, RussianCase);
  • круглые разряды дают тысячный, двухтысячный, миллионный и т. п.
using StringFunctions.Russian;

Console.WriteLine(RussianOrdinalToWords.Convert(2026)); // "две тысячи двадцать шестой"
Console.WriteLine(RussianOrdinalToWords.Convert(2026, RussianGender.Masculine, RussianCase.Genitive));
// "две тысячи двадцать шестого"
Console.WriteLine(21.ToRussianOrdinal(RussianGender.Neuter)); // "двадцать первое"

9. Дата прописью (русский язык)

Класс RussianDateToWords (пространство имён StringFunctions.Russian) записывает дату прописью.

  • день — порядковое среднего рода, месяц — в родительном падеже, год — порядковое в родительном со словом «года»;
  • падеж дня выбирается параметром (именительный или родительный);
  • есть перегрузки по (year, month, day), DateOnly и DateTime.
using StringFunctions.Russian;

Console.WriteLine(RussianDateToWords.Convert(2026, 6, 19).Value);
// "девятнадцатое июня две тысячи двадцать шестого года"

Console.WriteLine(RussianDateToWords.Convert(2026, 6, 19, RussianCase.Genitive).Value);
// "девятнадцатого июня две тысячи двадцать шестого года"

Console.WriteLine(new DateOnly(2025, 3, 1).ToRussianWords().Value);
// "первое марта две тысячи двадцать пятого года"

10. Разбор числительного из слов в число (русский язык)

Класс RussianNumberParser (пространство имён StringFunctions.Russian) выполняет обратную операцию к RussianNumberToWords — превращает количественное числительное из слов в число.

  • поддерживаются все падежи и роды, приставка «минус» и слово «ноль»;
  • регистр, буква «ё»/«е» и лишние пробелы не важны;
  • разбор строгий: любое нераспознанное слово или некорректный порядок → Failure.
using StringFunctions.Russian;

Console.WriteLine(RussianNumberParser.Parse("сто двадцать три").Value);   // 123
Console.WriteLine(RussianNumberParser.Parse("двадцати трёх").Value);       // 23
Console.WriteLine("минус пять".ParseRussianNumber().Value);                 // -5

Генератор и парсер симметричны: Parse(Convert(n)) == n для любого числа и падежа.


11. Время прописью — «цифровой» формат (русский язык)

Класс RussianTimeToWords (пространство имён StringFunctions.Russian) записывает время прописью в официальном (24-часовом) виде.

  • часы согласуются со словом «час», минуты — со словом «минута» (женский род);
  • поведение при нулевых минутах задаётся параметром RussianTimeZeroMinutes (опускать либо «ровно»);
  • есть перегрузка по TimeOnly.
using StringFunctions.Russian;

Console.WriteLine(RussianTimeToWords.Convert(15, 30).Value);  // "пятнадцать часов тридцать минут"
Console.WriteLine(RussianTimeToWords.Convert(15, 0).Value);   // "пятнадцать часов"
Console.WriteLine(RussianTimeToWords.Convert(15, 0, RussianTimeZeroMinutes.Sharp).Value);
// "пятнадцать часов ровно"
Console.WriteLine(new TimeOnly(9, 5).ToRussianWords().Value); // "девять часов пять минут"

12. Время прописью — разговорный формат (русский язык)

Класс RussianColloquialTimeToWords (пространство имён StringFunctions.Russian) записывает время в разговорном (неявно 12-часовом) виде.

  • до получаса минуты считаются в следующий час, после — на убыль;
  • «четверть», «половина», «без четверти»;
  • по запросу добавляется часть суток («утра», «дня», «вечера», «ночи»).
using StringFunctions.Russian;

Console.WriteLine(RussianColloquialTimeToWords.Convert(6, 5).Value);   // "пять минут седьмого"
Console.WriteLine(RussianColloquialTimeToWords.Convert(6, 30).Value);  // "половина седьмого"
Console.WriteLine(RussianColloquialTimeToWords.Convert(6, 45).Value);  // "без четверти семь"
Console.WriteLine(RussianColloquialTimeToWords.Convert(15, 0, includePartOfDay: true).Value);
// "три часа дня"
Console.WriteLine(new TimeOnly(6, 45).ToRussianColloquialWords().Value); // "без четверти семь"

Реализованы все задуманные модули работы с числительными, датой и временем.


Краткий контракт IntRangeParser

Поддерживаемый формат

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

Правила для нуля

  • 0 допустим;
  • 0-N допустим;
  • 0- допустим;
  • -N всегда означает диапазон от 1, а не от 0.

Ошибки

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

  • rangeSource == null;
  • maxRangeValue < 0;
  • токен имеет некорректный формат;
  • явно указано значение меньше 0;
  • граница выходит за пределы 0..maxRangeValue;
  • -N используется не первым токеном;
  • N- используется не последним токеном.

Краткий контракт IntRangeFormatter

Formatter принимает произвольную последовательность IEnumerable<int> и возвращает нормализованную строку диапазонов.

Правила

  • отрицательные значения недопустимы;
  • вход может быть неотсортированным;
  • дубли допустимы;
  • перед форматированием вход сортируется и дедуплицируется;
  • соседние значения объединяются в диапазоны.

Открытые диапазоны

Во второй перегрузке при useOpenRanges = true могут использоваться:

  • -N для диапазона 1..N;
  • N- для диапазона N..maxRangeValue;
  • 0- для диапазона 0..maxRangeValue.

Ограничения и безопасность

Библиотека выполняет только вычисления над строками: нет ввода-вывода, сети, рефлексии, десериализации и запуска процессов. Поэтому классических векторов внедрения (инъекций, RCE) у неё нет. Единственный практический риск при работе с недоверенным вводом — потребление памяти, и его стоит учитывать:

  • IntRangeParser.Parse возвращает список всех значений диапазона, поэтому объём выделяемой памяти пропорционален мощности результата, а не длине входной строки. Запрос вида "1-" или "0-N" при большом maxRangeValue может потребовать сотни мегабайт и более.
  • Если запрошенный набор не помещается в List<int> (превышает Array.MaxLength), метод возвращает Failure, а не бросает исключение.
  • Рекомендация: при разборе данных из недоверенного источника ограничивайте maxRangeValue разумным значением на стороне вызывающего кода. Библиотека намеренно не навязывает собственный жёсткий лимит, чтобы не ограничивать легитимные сценарии.

IntRangeFormatter и NormalizeString работают за один проход и выделяют память пропорционально размеру входных данных.


Подключение

Вариант 1. Через исходный код / ProjectReference

<ProjectReference Include="..\StringFunctions\StringFunctions.csproj" />

Вариант 2. Через пакет

После публикации библиотеку можно будет подключать как пакет NuGet.


Тестирование

Для библиотеки добавлены автоматические тесты.

Покрываются:

  • parser;
  • formatter;
  • проверка баланса скобок;
  • нормализация строк.

Запуск тестов:

dotnet test

Документация

Дополнительные материалы:

Также для публичного API добавлены XML-комментарии.


Лицензия

См. файл LICENSE.


Статус проекта

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

Основные недавние изменения:

  • добавлен IntRangeParser;
  • добавлен IntRangeFormatter;
  • публичный API приведён к стилю ResultType;
  • добавлены тесты и документация.
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Ti-Soft.StringFunctions:

Package Downloads
Ti-Soft.SearchEngine

Лёгкая embedded-библиотека для нечёткого и фонетического поиска по строковым полям БД на русском языке с Result-based API.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.7.0 54 6/19/2026
2.6.0 52 6/19/2026
2.5.0 63 6/19/2026
2.4.0 54 6/19/2026
2.3.0 51 6/19/2026
2.2.0 54 6/18/2026
2.1.0 51 6/18/2026
2.0.0 314 3/30/2026
1.9.0 115 3/30/2026