При разработке алгоритмов обработки текста или загрузки данных из внешних файлов программистам 1С часто приходится анализировать текстовые строки, содержащие список чисел. Зачастую возникает специфическая задача: необходимо посчитать количество только целых чисел в исходном тексте.
Сложность заключается в том, что целое число может быть записано в строке не только в классическом виде (например, "2", "3", "4"), но и с явно указанной дробной частью, состоящей исключительно из нулей (например, "1.0" или "1,0"). При этом строки вида "5.13" содержат полноценную дробную часть и считаться целыми числами не должны.
В этой статье мы подробно разберем, как правильно реализовать алгоритм подсчета целых чисел на встроенном языке 1С:Предприятие, проанализируем подводные камни при работе с типом Число, а также рассмотрим альтернативный способ решения через регулярные выражения.
Рассмотрим типичную ошибку при разборе строки с числами. Когда разработчик использует встроенную функцию Число() внутри конструкции Попытка ... Исключение, он сталкивается со следующими нюансами:
Число() в платформе 1С считает корректным разделителем дробной части только точку (например, "1.0"). Если в строке передается значение с запятой (например, "1,0"), то вызов Число("1,0") вызывает исключение («Преобразование к типу Число не может быть выполнено»). Значение попадает в блок Исключение и не учитывается.Попытка ... Исключение написана некорректно или находится вне тела цикла, первая же ошибка преобразования текста (например, числа "5.13" или некорректного символа) может прервать обход массива или алгоритма.СтрРазделить() с неправильными параметрами может приводить к созданию пустых элементов массива, если между числами содержится несколько пробелов подряд или пробелы находятся в начале и конце строки.Выясним, как написать устойчивый код, который корректно обработает все эти ситуации.
Проанализируем базовый и наиболее наглядный алгоритм. Нам потребуется разделить исходную строку на массив подстрок по пробелу, привести каждую подстроку к числовому виду и проверить, равна ли целая часть числа самому числу.
Разберем алгоритм по шагам:
СокрЛП().СтрЗаменить().СтрРазделить(), передав третьим параметром значение Истина, чтобы проигнорировать пустые подстроки.Число.Цел(). Если Цел(ТекущееЧисло) = ТекущееЧисло, значит перед нами целое число (включая записи вида 1.0).Посмотрим на готовый пример функции в 1С:
Функция КоличествоЦелыхЧиселВСтроке(Знач ИсходнаяСтрока) Экспорт
КоличествоЧисел = 0;
// Приводим запятые к точкам для корректной работы функции Число()
ПодготовленнаяСтрока = СтрЗаменить(СокрЛП(ИсходнаяСтрока), ",", ".");
// Разбиваем строку по пробелам, пропуская пустые элементы
МассивПодстрок = СтрРазделить(ПодготовленнаяСтрока, " ", Истина);
Для Каждого ТекЭлемент Из МассивПодстрок Цикл
Попытка
ТекущееЧисло = Число(ТекЭлемент);
// Проверяем, равна ли целая часть самому числу
Если Цел(ТекущееЧисло) = ТекущееЧисло Тогда
КоличествоЧисел = КоличествоЧисел + 1;
КонецЕсли;
Исключение
// Если элемент не является числом, просто переходим к следующему
Продолжить;
КонецПопытки;
КонецЦикла;
Возврат КоличествоЧисел;
КонецФункции
Протестируем данную функцию на примере строки из обращения пользователя:
ТестоваяСтрока = " 2 3 4 0.1 5.13 1,0 ";
Результат = КоличествоЦелыхЧиселВСтроке(ТестоваяСтрока);
Предупреждение("В строке " + Строка(Результат) + " целых чисел");
Данный код вернет результат 4 (будут учтены числа 2, 3, 4 и 1,0, а дробные 0.1 и 5.13 пропущены).
Альтернативный способ проверки числа на целочисленность в 1С после успешного преобразования — использование математического оператора остатка от деления %.
Если число является целым, то остаток от его деления на 1 всегда равен нулю (ТекущееЧисло % 1 = 0). Это лаконичное условие заменяет вызов функции Цел().
Рассмотрим пример реализации:
Функция КоличествоЦелыхЧиселЧерезОстаток(Знач ИсходнаяСтрока) Экспорт
КоличествоЧисел = 0;
// Нормализуем непечатные символы и пробелы
ОбработаннаяСтрока = СтрЗаменить(ИсходнаяСтрока, Символы.ПС, " ");
ОбработаннаяСтрока = СтрЗаменить(ОбработаннаяСтрока, Символы.НПП, " ");
ОбработаннаяСтрока = СтрЗаменить(ОбработаннаяСтрока, ",", ".");
МассивЭлементов = СтрРазделить(СокрЛП(ОбработаннаяСтрока), " ", Истина);
Для Каждого СтрЗначение Из МассивЭлементов Цикл
Попытка
ЗначениеЧисло = Число(СтрЗначение);
// Проверка остатка от деления на 1
Если ЗначениеЧисло % 1 = 0 Тогда
КоличествоЧисел = КоличествоЧисел + 1;
КонецЕсли;
Исключение
// Не числовой элемент
КонецПопытки;
КонецЦикла;
Возврат КоличествоЧисел;
КонецФункции
Данная проверка работает одинаково корректно как для положительных чисел (10.0 % 1 = 0), так и для отрицательных целых чисел (-3.0 % 1 = 0).
Если исходный текст имеет большой объем или содержит множество разнородных символов (буквы, спецсимволы, переводы строк), постоянное провоцирование генерации исключений внутри блока Попытка ... Исключение может снижать производительность.
В таких случаях наилучшим решением является использование COM-объекта VBScript.RegExp. Регулярное выражение позволяет сразу найти все паттерны целых чисел в строке без разбивки на массив и без вызова функции Число().
Проанализируем регулярное выражение для этой задачи:
(?<=\s|^)-?\d+([.,]0+)?(?=\s|$)
(?<=\s|^) — просмотр назад: перед числом должен быть пробельный символ или начало строки.-? — необязательный знак минуса для отрицательных чисел.\d+ — одна или несколько цифр.([.,]0+)? — необязательная группа: точка или запятая, за которой следуют только нули.(?=\s|$) — просмотр вперед: после числа должен следовать пробельный символ или конец строки.Рассмотрим пример реализации на 1С:
Функция ПосчитатьЦелыеЧислаRegExp(Знач ИсходнаяСтрока) Экспорт
Попытка
RegExp = Новый COMОбъект("VBScript.RegExp");
Исключение
ЗаписьЖурналаРегистрации("Ошибка RegEx", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
Возврат 0;
КонецПопытки;
RegExp.Global = Истина;
RegExp.IgnoreCase = Истина;
RegExp.Multiline = Истина;
// Шаблон поиска целых чисел, в т.ч. с нулями в дробной части
RegExp.Pattern = "(?<=^|\s)-?\d+([.,]0+)?(?=\s|$)";
Совпадения = RegExp.Execute(ИсходнаяСтрока);
Возврат Совпадения.Count;
КонецФункции
Этот подход работает мгновенно даже на очень больших текстах, игнорируя любые сторонние символы и не требуя предварительной подмены запятых на точки.
Разберем, какой метод выбрать в зависимости от требований вашего проекта:
VBScript.RegExp не доступен на серверах под управлением ОС Linux.Главные правила при работе со строками чисел в 1С:
СтрЗаменить(Строка, ",", ".") перед передачей подстроки в функцию Число().СтрРазделить() всегда передавайте третий параметр как Истина, чтобы отсекать пустые подстроки при двойных пробелах.Попытка ... Исключение строго внутри цикла обхода элементов, чтобы ошибка в одном элементе не прерывала обработку остальных данных.