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

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

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

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

Способ 1. Потоковое чтение через объект «ЧтениеТекста» (наиболее производительный вариант)

Когда требуется обработать множество файлов в цикле, загрузка их содержимого целиком в память строковыми переменными создает избыточную нагрузку на систему. Оптимальным решением является использование объекта ЧтениеТекста с последовательным чтением через метод ПрочитатьСтроку().

Для корректного отсечения заголовков и маркеров организуем конечный автомат с флагом состояния:

  1. Открываем файл для чтения и построчно считываем данные в цикле Пока.
  2. Проверяем начало блока с помощью метода СтрНачинаетсяС(). Как только строка начинается со слова «Start», переключаем флаг сбора данных.
  3. Пропускаем пустую строку, следующую сразу за стартовым заголовком, используя проверку ПустаяСтрока().
  4. Накапливаем полезные строки в результирующий массив или строку до тех пор, пока не встретится маркер «Finish».
  5. Обязательно закрываем объект чтения методом Закрыть(), чтобы освободить дескриптор файла в операционной системе.

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


Чтение = Новый ЧтениеТекста(ПутьКФайлу, КодировкаТекста.UTF8);
МассивРезультата = Новый Массив;
БлокНайден = Ложь;
ПропуститьПустую = Ложь;

Стр = Чтение.ПрочитатьСтроку();
Пока Стр <> Неопределено Цикл
    Если Не БлокНайден Тогда
        Если СтрНачинаетсяС(СокрЛП(Стр), "Start") Тогда
            БлокНайден = Истина;
            ПропуститьПустую = Истина;
        КонецЕсли;
    Иначе
        Если СтрНачинаетсяС(СокрЛП(Стр), "Finish") Тогда
            Прервать;
        КонецЕсли;
        
        Если ПропуститьПустую И ПустаяСтрока(Стр) Тогда
            ПропуститьПустую = Ложь;
        Иначе
            ПропуститьПустую = Ложь;
            МассивРезультата.Добавить(Стр);
        КонецЕсли;
    КонецЕсли;
    
    Стр = Чтение.ПрочитатьСтроку();
КонецЦикла;

Чтение.Закрыть();
РезультатТекст = СтрСоединить(МассивРезультата, Символы.ПС);

Способ 2. Быстрое разбиение на массив через «СтрРазделить» (для платформы 8.3)

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

Начиная с версии платформы 8.3.6, гораздо эффективнее разбить весь текст на массив строк с помощью функции СтрРазделить(), где доступ к элементам осуществляется мгновенно.

Рассмотрим алгоритм разбора массива строк:


МассивВсехСтрок = СтрРазделить(ИсходныйТекст, Символы.ПС, Ложь);
РезультатМассив = Новый Массив;
ФлагСбора = Ложь;
ОжидаемПропуск = Ложь;

Для Каждого ТекущаяСтрока Из МассивВсехСтрок Цикл
    ОчищеннаяСтрока = СокрЛП(ТекущаяСтрока);
    
    Если Не ФлагСбора Тогда
        Если СтрНачинаетсяС(ОчищеннаяСтрока, "Start") Тогда
            ФлагСбора = Истина;
            ОжидаемПропуск = Истина;
        КонецЕсли;
        Продолжить;
    КонецЕсли;
    
    Если СтрНачинаетсяС(ОчищеннаяСтрока, "Finish") Тогда
        Прервать;
    КонецЕсли;
    
    Если ОжидаемПропуск И ПустаяСтрока(ОчищеннаяСтрока) Тогда
        ОжидаемПропуск = Ложь;
        Продолжить;
    КонецЕсли;
    
    ОжидаемПропуск = Ложь;
    РезультатМассив.Добавить(ТекущаяСтрока);
КонецЦикла;

СтрокаРезультат = СтрСоединить(РезультатМассив, Символы.ПС);

Способ 3. Использование регулярных выражений (COM-объект VBScript.RegExp)

В среде под управлением ОС Windows задачу извлечения фрагмента переменной длины между двумя сигнатурами можно решить в несколько строк без явных циклов с помощью регулярных выражений.

Проанализируем шаблон регулярного выражения:

  1. Шаблон Start\s+\d+ находит стартовый маркер и следующее за ним число любой разрядности.
  2. Символы \r?\n\s*\r?\n учитывают обязательный перевод строки и следующую за ним пустую строку.
  3. Группа захвата ([\s\S]*?) лениво забирает весь полезный текст до завершающего маркера.
  4. Маркер \r?\nFinish обозначает границу окончания блока.

Посмотрим на пример кода с использованием объекта VBScript.RegExp:


РегВыр = Новый COMОбъект("VBScript.RegExp");
РегВыр.Pattern = "Start\s+\d+\s*\r?\n\s*\r?\n([\s\S]*?)\r?\nFinish";
РегВыр.IgnoreCase = Истина;
РегВыр.Multiline = Истина;

Совпадения = РегВыр.Execute(ИсходныйТекст);
Если Совпадения.Count > 0 Тогда
    СтрокаРезультат = Совпадения.Item(0).SubMatches.Item(0);
Иначе
    СтрокаРезультат = "";
КонецЕсли;

Важное ограничение: данный способ работает исключительно на серверах и клиентах под управлением Windows, так как использует технологию COM.

Важные нюансы работы с текстовыми данными

  1. Переносы строк: Файлы могут содержать окончания строк в формате Windows (CRLF, \r\n) или Linux (LF, \n). При разбиении только по Символы.ПС в конце строк может оставаться невидимый символ возврата каретки Символы.ВК. Для его удаления применяйте функцию СокрЛП() или СтрЗаменить(Стр, Символы.ВК, "").
  2. Проверка на пустоту: Простое сравнение Стр = "" часто дает сбой, если в пустой строке присутствуют пробелы или знаки табуляции. Всегда используйте стандартную функцию платформы ПустаяСтрока(Стр).
  3. Поиск подстроки: Вместо универсальной функции Найти() рекомендуется использовать строгие проверки СтрНачинаетсяС() или СтрЗаканчиваетсяНа(). Это исключает случайное срабатывание маркера, если слово «Start» или «Finish» встретится внутри самого информационного блока.
← На главную