Как найти наибольшее число в строке в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Давайте подробно разберем все способы решения этой задачи, проанализируем возможные ошибки и напишем надежный и безопасный код на встроенном языке 1С.

Способ 1: Классический циклический обход массива

Наиболее надежный, предсказуемый и безопасный алгоритм — это разбор строки на подстроки с последующим конвертированием в числа. Рассмотрим по шагам, как это реализовать.

  1. Разделим исходную строку на массив с помощью функции СтрРазделить.
  2. Обойдем полученный массив в цикле.
  3. Преобразуем строковые элементы в числовой тип с помощью функции Число.
  4. Сравним каждое полученное значение с текущим максимумом, используя функцию Макс.

Здесь важно учесть две критические ловушки, в которые часто попадают разработчики.

Ловушка инициализации максимума: Интуитивно хочется объявить переменную МаксЧисло = 0 до начала цикла. Но проанализируем ситуацию, когда пользователь введет строку, состоящую только из отрицательных чисел (например, "-10 -25 -5"). Наш алгоритм вернет ноль, хотя такого числа даже не было в строке! Поэтому стартовое значение должно инициализироваться первым обработанным элементом.

Ловушка региональных настроек: Пользователь может вводить десятичные дроби как через запятую, так и через точку. Платформа 1С строго привязана к локали. Если разделителем дробной части в базе считается запятая, функция Число выдаст ошибку преобразования на строке "45.5". Обязательно нормализуем строку до конвертации.

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


Процедура НайтиМаксимумВСтроке(СтрокаПользователя)
    
    МассивПодстрок = СтрРазделить(СтрокаПользователя, " ", Ложь);
    МаксимальноеЗначение = Неопределено;
    
    Для Каждого СтроковоеЗначение Из МассивПодстрок Цикл
        
        // Заменяем возможные точки на запятые для корректного преобразования в дробь
        НормализованнаяСтрока = СтрЗаменить(СтроковоеЗначение, ".", ",");
        
        Попытка
            ТекущееЧисло = Число(НормализованнаяСтрока);
        Исключение
            Продолжить; // Игнорируем текст и случайный мусор
        КонецПопытки;
        
        Если МаксимальноеЗначение = Неопределено Тогда
            МаксимальноеЗначение = ТекущееЧисло; // Инициализация первым реальным числом
        Иначе
            МаксимальноеЗначение = Макс(МаксимальноеЗначение, ТекущееЧисло);
        КонецЕсли;
        
    КонецЦикла;
    
    Сообщить("Максимальное число: " + МаксимальноеЗначение);
    
КонецПроцедуры

Способ 2: Использование алгоритмов сортировки списка значений

Рассмотрим альтернативный подход, который избавляет нас от необходимости писать логику сравнения вручную. Мы можем преобразовать элементы строки в числа, поместить их в объект СписокЗначений и отсортировать его по убыванию. Первое значение в таком списке автоматически станет максимальным.


Процедура МаксимумЧерезСписокЗначений(СтрокаПользователя)
    
    СписокЧисел = Новый СписокЗначений;
    МассивПодстрок = СтрРазделить(СтрокаПользователя, " ", Ложь);
    
    Для Каждого Элемент Из МассивПодстрок Цикл
        Попытка
            СписокЧисел.Добавить(Число(СтрЗаменить(Элемент, ".", ",")));
        Исключение 
            // Игнорируем ошибки приведения типов
        КонецПопытки;
    КонецЦикла;
    
    Если СписокЧисел.Количество() > 0 Тогда
        СписокЧисел.СортироватьПоЗначению(НаправлениеСортировки.Убыв);
        МаксимальноеЗначение = СписокЧисел[0].Значение;
        Сообщить("Наибольшее число: " + МаксимальноеЗначение);
    КонецЕсли;

КонецПроцедуры

Лайфхак для ускорения: Если ваша инфраструктура работает в среде ОС Windows, вместо объекта 1С СписокЗначений целесообразно использовать COM-объект System.Collections.ArrayList. Он гораздо более легковесный и выполняет метод .Sort() значительно быстрее на больших массивах данных, так как не тянет за собой визуальный интерфейс, свойственный объектам платформы.

Способ 3: Динамическое вычисление (опасно!)

На форумах часто предлагают "изящное" решение в одну строку с использованием метода Вычислить. Логика такова: мы заменяем пробелы в строке на запятые, оборачиваем результат в вызов функции Макс() и просим платформу исполнить эту строку как программный код.

Например, из строки "25 45 13" мы формируем выражение Макс(25, 45, 13).

Многие разработчики совершают ошибку, пытаясь передать строку целиком: МаксимальноеЗначение = Макс("25, 45, 13"). Синтакс-помощник гласит, что функция Макс принимает перечень значений. Передав единую строку, вы получите ее же обратно. Именно поэтому используется метод вычисления кода в рантайме:


СтрокаДляПоиска = СтрЗаменить(СтрокаПользователя," ", ",");
МаксимальноеЗначение = Вычислить("Макс(" + СтрокаДляПоиска + ")");

Почему мы настоятельно не рекомендуем этот метод? Выясним причину.

  1. Уязвимость внедрения кода (Code Injection): Функция Вычислить компилирует и исполняет любой переданный ей текст. Если злоумышленник (или невнимательный пользователь) введет в поле не числа, а вредоносный код, вызывающий удаление данных или запуск внешних обработок, система послушно попытается это выполнить.
  2. Ограничения безопасного режима: В современных типовых конфигурациях серверный код часто исполняется в профилях безопасности, где использование метода Вычислить просто запрещено.
  3. Лимиты платформы 1С: Платформа имеет жесткие ограничения на длину компилируемого выражения и количество параметров в функциях. Если пользователь скопирует и вставит тысячу чисел через пробел, метод упадет с критической ошибкой переполнения стека.

Использовать этот метод можно только в сугубо личных утилитах с полным контролем входящих данных.

Способ 4: Извлечение чисел из "грязных" строк с помощью RegExp

Давайте проанализируем сложную ситуацию. Что, если строка, которую ввел пользователь, содержит не только числа и пробелы, но и буквы, символы табуляции, переносы строк, единицы измерения (например: "Вес товара 25 кг, брутто 45.5 кг, тара -2 кг")? Стандартная функция СтрРазделить в такой ситуации потребует сложной многоступенчатой очистки текста.

Для надежного решения воспользуемся регулярными выражениями через COM-объект VBScript.RegExp. Напишем паттерн, который будет находить все числа в строке (включая отрицательные и дроби) и игнорировать весь остальной текст.


Процедура НайтиМаксимумВГрязнойСтроке(СтрокаПользователя)
    
    RegExp = Новый COMОбъект("VBScript.RegExp");
    RegExp.Global = Истина; // Искать все совпадения, а не только первое
    RegExp.IgnoreCase = Истина;
    
    // Паттерн означает: опциональный минус, затем одна или более цифр, 
    // затем опциональная дробная часть с точкой или запятой
    RegExp.Pattern = "-?\d+([.,]\d+)?"; 
    
    Matches = RegExp.Execute(СтрокаПользователя);
    МаксимальноеЗначение = Неопределено;
    
    Для Сч = 0 По Matches.Count - 1 Цикл
        
        НайденнаяПодстрока = Matches.Item(Сч).Value;
        ТекущееЧисло = Число(СтрЗаменить(НайденнаяПодстрока, ".", ","));
        
        Если МаксимальноеЗначение = Неопределено Тогда
            МаксимальноеЗначение = ТекущееЧисло;
        Иначе
            МаксимальноеЗначение = Макс(МаксимальноеЗначение, ТекущееЧисло);
        КонецЕсли;
        
    КонецЦикла;
    
    Если МаксимальноеЗначение <> Неопределено Тогда
        Сообщить("Найдено наибольшее число: " + МаксимальноеЗначение);
    Иначе
        Сообщить("В строке не найдено ни одного числа.");
    КонецЕсли;

КонецПроцедуры

Этот подход гарантирует, что программа не сломается от случайных двойных пробелов, неразрывных пробелов (символ с кодом 160, часто копируемый из интернета) или букв, оказавшихся внутри числового ряда.

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

← На главную