Как программно заменить многострочный фрагмент в тексте запроса 1С?

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

При доработке типовых конфигураций 1С программисты часто сталкиваются с необходимостью динамически изменить текст существующего запроса. Если этот запрос простой и однострочный, задача решается стандартной функцией СтрЗаменить. Однако, когда требуется подменить сложную многострочную конструкцию (например, массивный оператор ВЫБОР...КОНЕЦ), разработчики часто обнаруживают, что функция замены отрабатывает впустую и текст остается прежним.

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

Причина проблемы: Невидимые символы форматирования

Основная причина, по которой функция СтрЗаменить или СтрНайти не обнаруживает нужный фрагмент в тексте запроса, кроется в невидимых символах разметки. К ним относятся:

Рассмотрим подробнее три практических метода обхода этих ограничений.

Способ 1. Нормализация текста запроса и точное копирование

Если вы хотите продолжить использовать стандартную функцию СтрЗаменить, вам необходимо гарантировать стопроцентное совпадение всех невидимых символов. Для этого выполним предварительную очистку («причесывание») текста запроса.

Разберем этот процесс по шагам:

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

Посмотрим на пример практической реализации данного подхода:


// Шаг 1. Нормализуем текст запроса, избавляясь от CRLF в пользу LF
ТекстЗапроса = СтрЗаменить(Запрос.Текст, Символы.ВК, "");

// Шаг 2. Задаем шаблон для поиска с использованием Символы.ПС
СтрокаНачис = "СУММА(ВЫБОР" + Символы.ПС +
"            КОГДА СведенияОДоходах.ЗастрахованФСС" + Символы.ПС +
"                ТОГДА СведенияОДоходах.ОбъектОбложенияВзносамиСтатья7 - СведенияОДоходах.ДенежноеДовольствиеВоеннослужащих" + Символы.ПС +
"            КОГДА СведенияОДоходах.ЗастрахованФССкакИностранец" + Символы.ПС +
"                ТОГДА СведенияОДоходах.ОбъектОбложенияВзносамиСтатья7 - СведенияОДоходах.ДенежноеДовольствиеВоеннослужащих" + Символы.ПС +
"            ИНАЧЕ 0" + Символы.ПС +
"        КОНЕЦ) КАК Начислено,";

СтрокаЗамены = "СУММА(СведенияОДоходах.ОбъектОбложенияВзносамиСтатья7 - СведенияОДоходах.ДенежноеДовольствиеВоеннослужащих) КАК Начислено,";

// Шаг 3. Производим замену в нормализованном тексте
Запрос.Текст = СтрЗаменить(ТекстЗапроса, СтрокаНачис, СтрокаЗамены);

Способ 2. Использование регулярных выражений (RegExp)

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

Начиная с версии платформы 1С:Предприятие 8.3.23, в системе появился встроенный полнофункциональный механизм работы с регулярными выражениями. Это кроссплатформенное решение, которое работает как на Windows, так и на серверах под управлением Linux.

Рассмотрим, как заменить сложный многострочный блок ВЫБОР...КОНЕЦ с помощью новой встроенной функции СтрЗаменитьПоРегулярномуВыражению:


// Задаем регулярное выражение для поиска
// Нам нужно найти конструкцию СУММА(ВЫБОР ... КОНЕЦ) КАК Начислено
// Шаблон \s* соответствует любому количеству пробелов, табуляций и переносов строк.
// Символы [\s\S]+? означают поиск любого символа (включая перенос строки) в минимальном объеме до слова КОНЕЦ.

ШаблонПоиска = "СУММА\s*\(\s*ВЫБОР[\s\S]+?КОНЕЦ\s*\)\s*КАК\s*Начислено\s*,";

СтрокаЗамены = "СУММА(СведенияОДоходах.ОбъектОбложенияВзносамиСтатья7 - СведенияОДоходах.ДенежноеДовольствиеВоеннослужащих) КАК Начислено,";

// Выполняем замену по регулярному выражению
Запрос.Текст = СтрЗаменитьПоРегулярномуВыражению(Запрос.Текст, ШаблонПоиска, СтрокаЗамены);

Если же ваша база работает на старых версиях платформы (ниже 8.3.23) под управлением операционной системы Windows, вы можете использовать COM-объект VBScript.RegExp. Проанализируем пример функции для такой замены:


Функция ВыполнитьЗаменуRegExp(Знач ИсходныйТекст, ШаблонПоиска, СтрокаЗамены) Экспорт
    Попытка
        RegExp = Новый COMОбъект("VBScript.RegExp");
        RegExp.IgnoreCase = Истина;
        RegExp.MultiLine  = Истина;
        RegExp.Global     = Истина;
        RegExp.Pattern    = ШаблонПоиска;
        
        Возврат RegExp.Replace(ИсходныйТекст, СтрокаЗамены);
    Исключение
        ЗаписьЖурналаРегистрации("РегулярныеВыражения", УровеньЖурналаРегистрации.Предупреждение,,, ОписаниеОшибки());
        Возврат ИсходныйТекст;
    КонецПопытки;
КонецФункции

Способ 3. Изменение через Схему Запроса (Рекомендуемый способ)

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

Самый надежный и профессиональный подход — это использование объекта СхемаЗапроса. Платформа самостоятельно разбирает текст запроса на составляющие элементы (дерево AST). В таком случае пробелы, переносы строк и форматирование вообще перестают иметь значение, так как вы будете работать напрямую со свойствами объектов схемы.

Разберем по шагам, как программно заменить выражение конкретного поля через схему:


// Шаг 1. Инициализируем схему запроса и загружаем в нее текущий текст
Схема = Новый СхемаЗапроса;
Схема.УстановитьТекстЗапроса(Запрос.Текст);

// Шаг 2. Получаем первый запрос из пакета запросов (обычно он единственный или главный)
ТекущийЗапрос = Схема.ПакетЗапросов[0];

// Шаг 3. Обходим все колонки запроса в поисках нужного псевдонима поля
КолонкаНайдена = Ложь;
Для Каждого Колонка Из ТекущийЗапрос.Колонки Цикл
    Если Колонка.Псевдоним = "Начислено" Тогда
        // Шаг 4. Устанавливаем новое выражение для найденного источника колонки
        Колонка.Источники[0].Выражение = "СУММА(СведенияОДоходах.ОбъектОбложенияВзносамиСтатья7 - СведенияОДоходах.ДенежноеДовольствиеВоеннослужащих)";
        КолонкаНайдена = Истина;
        Прервать;
    КонецЕсли;
КонецЦикла;

// Шаг 5. Получаем измененный текст обратно в объект Запрос
Если КолонкаНайдена Тогда
    Запрос.Текст = Схема.ПолучитьТекстЗапроса();
Иначе
    ВызватьИсключение "Не удалось найти поле 'Начислено' в схеме запроса для его программного изменения.";
КонецЕсли;

Этот способ гарантирует, что даже если исходное выражение внутри оператора ВЫБОР кардинально изменится, ваша программа безошибочно найдет поле по его псевдониму "Начислено" и заменит его логику на новую.

Важное правило: Всегда контролируйте результат замены

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

Чтобы избежать подобных ситуаций, используйте защитный код контроля:


// Запоминаем исходную длину текста запроса
ДлинаДоЗамены = СтрДлина(Запрос.Текст);

// Выполняем замену
Запрос.Текст = СтрЗаменить(Запрос.Текст, СтрокаНачис, СтрокаЗамены);

// Сравниваем длину до и после
Если ДлинаДоЗамены = СтрДлина(Запрос.Текст) Тогда
    // Замена не произошла, так как исходный фрагмент не был найден в тексте
    ВызватьИсключение "Внимание! Программная замена текста запроса не была выполнена. Обратитесь к разработчику.";
КонецЕсли;

Применяя данные подходы, вы сможете легко и безопасно модифицировать сложные многострочные запросы в любых механизмах платформы 1С:Предприятие.

← На главную