Как проверить корректность расстановки скобок в выражении?

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

Проверка корректности расстановки скобок в текстовой строке — это классическая задача программирования, которая часто встречается при написании парсеров, синтаксических анализаторов или в качестве тестового задания. В рамках платформы 1С:Предприятие мы можем решить эту задачу несколькими способами: от простого обхода строки со счетчиком до использования стека. Давайте подробно разберем, как реализовать надежный алгоритм проверки, который будет возвращать результат «правильно» или «неправильно».

Почему недостаточно простого подсчета количества скобок?

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

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

Алгоритм со счетчиком для круглых скобок

Если в условии задачи сказано, что выражение содержит только один тип скобок (в нашем случае — круглые), то самым оптимальным решением будет использование числового счетчика («баланса»). Выясним принцип его работы:

  1. Создаем переменную-счетчик и устанавливаем ее в значение 0.
  2. Начинаем посимвольный обход строки слева направо.
  3. Если встречаем открывающую скобку (, увеличиваем счетчик на 1.
  4. Если встречаем закрывающую скобку ), уменьшаем счетчик на 1.
  5. Важное условие: если в любой момент обхода счетчик стал отрицательным (меньше 0), это означает, что в выражении появилась закрывающая скобка, для которой не было открывающей. В этом случае мы сразу можем сказать, что выражение составлено неправильно.
  6. После завершения цикла проверяем финальное значение: если счетчик равен 0, значит все скобки закрыты правильно. Если больше 0 — остались незакрытые скобки.

Давайте реализуем этот алгоритм на языке 1С, учитывая требования к возвращаемому результату («правильно» или «неправильно»):


Функция ПроверитьКорректностьСкобок(Знач ТекстВыражения) Экспорт
    
    БалансСкобок = 0;
    
    // Перебираем каждый символ строки
    Для Позиция = 1 По СтрДлина(ТекстВыражения) Цикл
        
        Символ = Сред(ТекстВыражения, Позиция, 1);
        
        Если Символ = "(" Тогда
            БалансСкобок = БалансСкобок + 1;
        ИначеЕсли Символ = ")" Тогда
            БалансСкобок = БалансСкобок - 1;
        КонецЕсли;
        
        // Если закрывающих стало больше, чем открытых на данный момент
        Если БалансСкобок < 0 Тогда
            Возврат "неправильно";
        КонецЕсли;
        
    КонецЦикла;
    
    // Если после прохода баланс нулевой - все хорошо
    Если БалансСкобок = 0 Тогда
        Возврат "правильно";
    Иначе
        Возврат "неправильно";
    КонецЕсли;
    
КонецФункции

Нюансы реализации в 1С

В приведенном выше коде мы использовали функцию Сред() для извлечения символа. Стоит отметить, что в 1С нет встроенного итератора для строк типа Для каждого Символ Из Строка, поэтому использование цикла Для по индексам с функцией Сред() является стандартным и наиболее понятным подходом.

Проанализируем производительность: данный алгоритм имеет сложность O(n), где n — длина строки. Это означает, что он работает максимально быстро, проходя по тексту всего один раз. Для большинства арифметических выражений в 1С этого более чем достаточно.

Решение для нескольких типов скобок (стековый метод)

Если задача усложняется и нам нужно проверять не только круглые, но и квадратные [] или фигурные {} скобки, метод счетчика перестает работать. Например, выражение "([)]" даст нулевой баланс по обоим типам, но оно некорректно, так как нарушена вложенность.

Для решения таких задач мы применим структуру данных Стек. В 1С роль стека отлично выполнит объект Массив.

Рассмотрим шаги алгоритма:

  1. При нахождении открывающей скобки любого типа ((, [, {) мы добавляем ее в массив (в конец).
  2. При нахождении закрывающей скобки (), ], }) мы проверяем последний элемент массива.
  3. Если массив пуст или последняя открытая скобка не соответствует типу закрывающей (например, закрываем ], а последняя была (), то выражение неверно.
  4. Если типы совпали, удаляем последнюю открытую скобку из массива и продолжаем.

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


Функция ПроверитьВсеТипыСкобок(Знач ТекстВыражения) Экспорт
    
    СтекСкобок = Новый Массив;
    
    Для к = 1 По СтрДлина(ТекстВыражения) Цикл
        ТекущийСимвол = Сред(ТекстВыражения, к, 1);
        
        // Добавляем открывающие в стек
        Если ТекущийСимвол = "(" Или ТекущийСимвол = "[" Или ТекущийСимвол = "{" Тогда
            СтекСкобок.Добавить(ТекущийСимвол);
            
        // Обрабатываем закрывающие
        ИначеЕсли ТекущийСимвол = ")" Или ТекущийСимвол = "]" Или ТекущийСимвол = "}" Тогда
            
            Если СтекСкобок.Количество() = 0 Тогда
                Возврат "неправильно"; // Закрывающая без открывающей
            КонецЕсли;
            
            ПоследняяОткрытая = СтекСкобок[СтекСкобок.Количество() - 1];
            
            // Проверяем соответствие пар
            Если (ТекущийСимвол = ")" И ПоследняяОткрытая = "(") 
                Или (ТекущийСимвол = "]" И ПоследняяОткрытая = "[") 
                Или (ТекущийСимвол = "}" И ПоследняяОткрытая = "{") Тогда
                
                СтекСкобок.Удалить(СтекСкобок.Количество() - 1);
            Иначе
                Возврат "неправильно"; // Не тот тип скобки
            КонецЕсли;
            
        КонецЕсли;
    КонецЦикла;
    
    Возврат ?(СтекСкобок.Количество() = 0, "правильно", "неправильно");
    
КонецФункции

Использование системных возможностей 1С

Некоторые участники обсуждения предложили использовать встроенную функцию Вычислить(). Рассмотрим этот вариант. Действительно, платформа сама умеет проверять синтаксис при вычислении выражений.

Преимущество: Не нужно писать свой парсер, 1С проверит не только скобки, но и правильность написания функций.

Недостаток: Если в строке содержатся данные, которые 1С не может вычислить (например, переменные, которых нет в контексте, или просто произвольный текст), функция Вычислить() вызовет исключение, даже если скобки расставлены верно.

Поэтому метод с Попытка...Исключение и Вычислить() подходит только в том случае, если вы уверены, что внутри скобок находится валидный код на языке 1С. Для общей задачи проверки текста (например, математической формулы или текста в чате) лучше использовать описанный выше посимвольный обход.

Итоги

Подведем итог нашего анализа. Для проверки простого выражения с круглыми скобками достаточно завести одну переменную БалансСкобок. Это самое быстрое и экономное по памяти решение. Если же вы работаете со сложными вложенными конструкциями разных типов, ваш выбор — массив, работающий по принципу стека. Помните, что проверка только общего количества вхождений символов — это распространенная ошибка, которой стоит избегать.

← На главную