Как правильно использовать регулярные выражения в 1С для поиска подстрок и заполнения таблицы значений?

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

При обработке текстовых файлов, выгрузок и HTML-разметки разработчики часто сталкиваются с нестандартными форматами строк. Стандартных функций платформы вроде Лев, Прав или СтрНайти в таких сценариях бывает недостаточно. В этой статье мы подробно разберем, как корректно извлекать данные по шаблону, почему стандартная процедура ЗаполнитьЗначенияСвойств не работает с объектами регулярных выражений, а также выясним, как оптимизировать цикл обхода найденных совпадений.

Анализ исходной проблемы и ошибок в коде

Рассмотрим типовую задачу: из текстового документа требуется найти все артикулы определенного формата (например, две группы по 4 цифры, разделенные косой чертой, вида 1234/5678) и выгрузить их в колонку таблицы значений.

В исходном примере разработчик попытался прочитать файл через ТекстовыйДокумент, применить COM-объект VBScript.RegExp и обойти коллекцию совпадений следующим образом:


ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Артикул");

Текст = Новый ТекстовыйДокумент;
Текст.Прочитать("C:\Users\User\Desktop\BT.htm");

RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.MultiLine = Истина;
RegExp.Global = Истина;
RegExp.IgnoreCase = Истина;
RegExp.Pattern = "[0-9]{4}/[0-9]{4}";

Совпадения = RegExp.Execute(Текст.ПолучитьТекст());

Для Каждого Совпадение Из Совпадения Цикл
    НовСтр = ТЗ.Добавить();
    ЗаполнитьЗначенияСвойств(НовСтр.Артикул, Совпадение);
КонецЦикла;

В результате таблица значений осталась пустой либо колонка заполнилась некорректными данными. Выясним причину этой ошибки:

  1. Неправильное применение метода ЗаполнитьЗначенияСвойств: процедура принимает в качестве первого параметра объект-приемник целиком (в данном случае строку таблицы НовСтр), а не конкретное поле НовСтр.Артикул.
  2. Несовпадение имен свойств: процедура ЗаполнитьЗначенияСвойств копирует значения только между одноименными реквизитами. Элемент коллекции Match COM-объекта VBScript.RegExp содержит свойства Value (найденный текст), FirstIndex (позиция вхождения) и Length (длина). Свойства с именем Артикул у COM-объекта нет, поэтому копирование не происходит.
  3. Особенности итератора Для Каждого для внешних коллекций: при работе со сторонними COM-объектами перебор через интерфейс итерации не всегда работает корректно в платформе 1С. Надежнее обращаться к элементам коллекции по числовому индексу.

Решение 1: Корректный перебор коллекции через VBScript.RegExp

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

Посмотрим на рабочий вариант с использованием классического цикла Для:


ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(20)));

Текст = Новый ТекстовыйДокумент;
Текст.Прочитать("C:\Users\User\Desktop\BT.htm");

RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.MultiLine = Истина;
RegExp.Global = Истина;
RegExp.IgnoreCase = Истина;
RegExp.Pattern = "\b\d{4}/\d{4}\b";

Совпадения = RegExp.Execute(Текст.ПолучитьТекст());

// Нумерация элементов COM-коллекции начинается с 0
КоличествоСовпадений = Совпадения.Count;
Для Индекс = 0 По КоличествоСовпадений - 1 Цикл
    ЭлементСовпадения = Совпадения.Item(Индекс);
    
    НовСтр = ТЗ.Добавить();
    НовСтр.Артикул = ЭлементСовпадения.Value;
КонецЦикла;

Обратите внимание на регулярное выражение: вместо [0-9]{4}/[0-9]{4} можно использовать более лаконичную запись \d{4}/\d{4}. Также мы добавили границы слов \b в начале и конце шаблона. Это исключит ложные срабатывания, если в тексте встретится длинная последовательность цифр, например 991234/567899.

Особенность вычисления выражений в цикле «Для ... По ... Цикл» в 1С

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

Разберем поведение платформы 1С:Предприятие. Синтаксис цикла с параметром устроен так, что выражение после ключевого слова По вычисляется строго один раз — в момент инициализации цикла. Значение фиксируется во внутренней памяти, и повторного обращения к свойству COM-объекта или вызова функции не происходит.

Проверим это на простом тестовом примере с выводом сообщения из функции:


Функция ПолучитьКоличествоИтераций()
    Сообщить("Вызов функции границы цикла!");
    Возврат 5;
КонецФункции

Процедура ТестВычисленияГраницы()
    Для Счетчик = 0 По ПолучитьКоличествоИтераций() - 1 Цикл
        // Тело цикла выполнится 5 раз
    КонецЦикла;
КонецПроцедуры

При выполнении этой процедуры сообщение «Вызов функции границы цикла!» появится в окне сообщений ровно один раз. Следовательно, синтаксис Для Индекс = 0 По Совпадения.Count - 1 Цикл является полностью безопасным с точки зрения накладных расходов, хотя предварительное сохранение значения в отдельную переменную делает код более наглядным при отладке.

Решение 2: Использование штатных функций платформы (начиная с 8.3.23)

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

Если ваша база работает на платформе 1С:Предприятие версии 8.3.23 и выше, настоятельно рекомендуется использовать встроенные методы работы с регулярными выражениями (стандарт ICU). Они работают быстро, полностью кроссплатформенно и не зависят от внешних библиотек.

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


ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(20)));

ТекстДокумент = Новый ТекстовыйДокумент;
ТекстДокумент.Прочитать("C:\Users\User\Desktop\BT.htm");
ИсходныйТекст = ТекстДокумент.ПолучитьТекст();

ШаблонПоиска = "\b\d{4}/\d{4}\b";

// Метод возвращает массив объектов типа РезультатПоискаПоРегулярномуВыражению
МассивРезультатов = СтрНайтиВсеПоРегулярномуВыражению(ИсходныйТекст, ШаблонПоиска);

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

Объект типа РезультатПоискаПоРегулярномуВыражению хранит координаты найденного блока: свойства НачальнаяПозиция и Длина. С помощью стандартной функции Сред мы получаем исходное строковое значение артикула и записываем его в строку таблицы значений без создания сторонних COM-объектов.

← На главную