При разработке решений в 1С мы часто сталкиваемся с необходимостью обработки строковых данных, которые приходят в различных форматах. Одна из типовых задач — это извлечение определенной подстроки из более длинной строки, когда эта подстрока окружена известными префиксами и суффиксами. Например, из строки "Label Some text_" нам нужно получить только "Some text". Для решения таких задач мощным и гибким инструментом являются регулярные выражения.
В этой статье мы подробно рассмотрим, как использовать регулярные выражения в 1С для извлечения нужных фрагментов текста, а также рассмотрим альтернативные подходы и современные возможности платформы.
COMОбъект("VBScript.RegExp") в 1СДо появления встроенной поддержки регулярных выражений в 1С:Предприятии 8.3.23, стандартным способом работы с ними было использование COM-объекта VBScript.RegExp. Этот метод до сих пор широко применяется в существующем коде и является важным для понимания.
Для нашей задачи – извлечения "Some text" из "Label Some text_" – мы можем использовать следующее регулярное выражение: Label\s*(.+)_. Давайте разберем этот шаблон по частям, чтобы понять, как он работает:
Label: Эта часть шаблона ищет точное соответствие слову "Label" в начале нашей целевой подстроки. Регулярные выражения чувствительны к регистру по умолчанию, но мы можем настроить это поведение.\s*: Данный элемент означает "ноль или более пробельных символов". Это позволяет нам учесть возможные варианты, когда после "Label" может быть один пробел, несколько пробелов или даже их отсутствие перед началом искомого текста.(.+): Это ключевая группа захвата.
() определяют группу захвата, содержимое которой мы хотим извлечь.. соответствует любому символу, кроме символа новой строки (перевода строки).+ означает "один или более" повторений предыдущего символа.(.+) означает "захватить один или более любых символов". Этот квантификатор является "жадным" (greedy), то есть он будет пытаться захватить как можно больше символов, прежде чем шаблон будет полностью соответствовать остальной части строки. В нашем случае, так как после группы захвата идет символ подчеркивания _, жадный поиск остановится ровно перед ним. Именно эта группа захвата и будет содержать наш искомый текст "Some text"._: Эта часть шаблона ищет точное соответствие символу подчеркивания, который является нашим конечным ограничителем.Теперь давайте посмотрим, как этот шаблон интегрируется в код 1С с использованием VBScript.RegExp:
Корень = "Label\s*(.+)_";
Многострочный = ЛОЖЬ; // Искать совпадения в одной строке
ИгнорироватьРегистр = ИСТИНА; // Игнорировать регистр символов при поиске
// Создаем объект регулярных выражений
RegExp = Новый COMОбъект("VBScript.RegExp");
// Устанавливаем свойства объекта RegExp
// Global = Истина - искать все совпадения в строке
RegExp.Global = Истина;
// Multiline = Многострочный - влияет на работу якорей ^ и $
RegExp.Multiline = Многострочный;
// IgnoreCase = ИгнорироватьРегистр - игнорировать регистр символов
RegExp.IgnoreCase = ИгнорироватьРегистр;
// Pattern - устанавливаем наш шаблон регулярного выражения
RegExp.Pattern = Корень;
// Исходная строка, в которой мы ищем
ТекстГдеИскать = "Label Some text_";
// Выполняем поиск совпадений
Вхождения = RegExp.Execute(ТекстГдеИскать);
// Перебираем найденные вхождения
Для каждого Вхождение Из Вхождения Цикл
// Вхождение.FirstIndex - начальная позиция совпадения
// Вхождение.Length - длина всего совпадения
// Вхождение.Value - все найденное совпадение (например, "Label Some text_")
// Вхождение.SubMatches(0) - это содержимое первой группы захвата
// В случае шаблона "Label\s*(.+)_", SubMatches(0) будет "Some text"
КореньЗначение = Вхождение.SubMatches(0);
// Выведем результат, например, в Сообщить()
Сообщить("Найденный фрагмент: " + КореньЗначение);
КонецЦикла;
В этом коде мы сначала определяем регулярное выражение в переменной Корень. Затем мы создаем объект RegExp и настраиваем его свойства, такие как Global (искать все совпадения), Multiline (режим многострочного поиска) и IgnoreCase (игнорировать регистр). Самое главное, мы устанавливаем наш шаблон в свойство Pattern.
Метод RegExp.Execute(ТекстГдеИскать) выполняет поиск и возвращает коллекцию найденных Вхождений. Каждое Вхождение представляет собой отдельное совпадение. Для получения содержимого группы захвата (того, что находится внутри скобок (.+)), мы обращаемся к коллекции Вхождение.SubMatches. Поскольку у нас одна группа захвата, ее индекс будет 0 (первая группа захвата в VBScript.RegExp всегда имеет индекс 0, а не 1, как в некоторых других движках). Таким образом, Вхождение.SubMatches(0) вернет нам искомый текст "Some text".
Хотя предложенный выше шаблон Label\s*(.+)_ отлично справляется с нашей задачей, существуют и другие, более гибкие или точные способы построения регулярных выражений, которые могут пригодиться в более сложных сценариях. Рассмотрим подробнее несколько из них.
(.+?)Как мы уже выяснили, квантификатор + по умолчанию является "жадным" (greedy) и пытается захватить как можно больше символов. В некоторых ситуациях это может привести к нежелательным результатам, если в строке присутствует несколько разделителей. Например, если бы строка была "Label Some text_ and more text_", жадный (.+) захватил бы "Some text_ and more text" до последнего символа _. Чтобы избежать этого и сделать поиск "нежадным" (non-greedy), мы добавляем знак вопроса ? после квантификатора, превращая его в +? или *?.
Пример нежадного регулярного выражения: Label\s*(.+?)_
Здесь (.+?) означает "захватить один или более любых символов, но как можно меньше". То есть, он будет захватывать символы до первого ближайшего символа _. В нашем случае с "Label Some text_" результат будет тем же ("Some text"), но в более сложных строках этот подход может быть более надежным.
Методы просмотров (lookarounds) позволяют нам указать, что наш искомый текст должен быть окружен определенными шаблонами, но при этом сами эти шаблоны не включаются в окончательный результат совпадения. Это может быть очень удобно, так как избавляет от необходимости использовать группы захвата и извлекать результат через SubMatches – сам результат совпадения уже будет искомой подстрокой.
Существуют два основных типа просмотров:
(?<=...)(?<=Label\s) означает "найти позицию, за которой следует 'Label ' (с пробелом)".(?=...)(?=_) означает "найти позицию, перед которой следует '_'".Комбинация просмотров: Мы можем объединить оба просмотра для нашей задачи:
(?<=Label\s).*(?=_)
Разберем это выражение:
(?<=Label\s): Убеждаемся, что перед искомым текстом находится "Label" с пробелом. Сам "Label " не будет частью результата..*: Соответствует нулю или более любым символам (кроме новой строки). Это наш искомый текст. Здесь * тоже по умолчанию жадный, но так как он ограничен просмотром вперед, то работает корректно.(?=_): Убеждаемся, что за искомым текстом следует символ подчеркивания _. Сам _ не будет частью результата.Преимущество: При использовании такого шаблона, результат самого совпадения (Вхождение.Value) будет непосредственно "Some text", без необходимости обращения к SubMatches(0). Это делает код более читаемым и иногда более эффективным.
Важное замечание: Поддержка просмотров назад (Positive Lookbehind) в движке VBScript.RegExp имеет свои ограничения, особенно в старых версиях 1С и Windows. В некоторых случаях они могут не работать или работать не так, как ожидается в более современных движках регулярных выражений. Всегда тестируйте такие шаблоны в вашей конкретной среде 1С.
Для простых и строго определенных задач, подобных нашей, использование регулярных выражений может быть избыточным. Встроенные функции языка 1С для работы со строками часто работают быстрее, так как не требуют создания COM-объекта и имеют меньшие накладные расходы. Если формат строки всегда одинаков и префиксы/суффиксы фиксированы, мы можем использовать комбинацию функций СтрНайти(), Сред() и СтрДлина().
Рассмотрим по шагам, как мы можем извлечь "Some text" из "Label Some text_" используя только стандартные функции 1С:
ИсходнаяСтрока = "Label Some text_";
НачальныйПрефикс = "Label "; // Префикс, который предшествует искомому тексту
КонечныйСуффикс = "_"; // Суффикс, который следует за искомым текстом
// 1. Находим позицию, с которой начинается наш искомый текст
// Сначала ищем начальный префикс "Label "
ПозицияНачалаПрефикса = СтрНайти(ИсходнаяСтрока, НачальныйПрефикс);
Если ПозицияНачалаПрефикса > 0 Тогда
// Если префикс найден, определяем реальную начальную позицию искомого текста
// Это позиция конца префикса, то есть начало нужной нам подстроки
ПозицияНачалаИскомогоТекста = ПозицияНачалаПрефикса + СтрДлина(НачальныйПрефикс);
// 2. Находим позицию, с которой начинается конечный суффикс
// Ищем суффикс "_", начиная поиск с позиции после нашего префикса
ПозицияНачалаСуффикса = СтрНайти(ИсходнаяСтрока, КонечныйСуффикс, , ПозицияНачалаИскомогоТекста);
Если ПозицияНачалаСуффикса > 0 Тогда
// 3. Вычисляем длину искомой подстроки
// Длина = (Позиция начала суффикса) - (Позиция начала искомого текста)
ДлинаПодстроки = ПозицияНачалаСуффикса - ПозицияНачалаИскомогоТекста;
// 4. Извлекаем подстроку
// Сред(ИсходнаяСтрока, НачальнаяПозиция, Длина)
Результат = Сред(ИсходнаяСтрока, ПозицияНачалаИскомогоТекста, ДлинаПодстроки);
Сообщить("Найденный текст (строковые функции): " + Результат); // Должно вывести "Some text"
Иначе
Сообщить("Конечный суффикс '" + КонечныйСуффикс + "' не найден.");
КонецЕсли;
Иначе
Сообщить("Начальный префикс '" + НачальныйПрефикс + "' не найден.");
КонецЕсли;
Этот подход имеет следующие преимущества:
Однако есть и недостатки:
Мы рекомендуем использовать встроенные строковые функции, когда задача очень проста и формат строки строго фиксирован, а регулярные выражения — для более сложных и гибких сценариев.
С выходом платформы 1С:Предприятие версии 8.3.23, разработчики получили значительное улучшение: встроенную поддержку регулярных выражений. Это означает, что теперь нет необходимости использовать COM-объект VBScript.RegExp, что делает код более нативным, надежным и кроссплатформенным. Новые функции предоставляют более удобный и современный способ работы с регулярными выражениями.
Для нашей задачи извлечения подстроки "Some text" из строки "Label Some text_" мы можем использовать новую функцию СтрНайтиПоРегулярномуВыражению(). Эта функция возвращает объект РезультатПоискаПоРегулярномуВыражению, который содержит информацию о совпадении, включая группы захвата.
Рассмотрим пример кода для платформы 1С:Предприятие 8.3.23 и выше:
СтрокаДляПоиска = "Label Some text_";
РегулярноеВыражение = "Label\s*(.+)_"; // Используем тот же шаблон
// Выполняем поиск по регулярному выражению
// Функция вернет первый найденный РезультатПоискаПоРегулярномуВыражению
РезультатПоиска = СтрНайтиПоРегулярномуВыражению(СтрокаДляПоиска, РегулярноеВыражение);
Если РезультатПоиска <> Неопределено Тогда
// Если совпадение найдено
// КоличествоГрупп() возвращает число групп захвата (скобок в шаблоне)
Если РезультатПоиска.КоличествоГрупп() > 0 Тогда
// Первая группа захвата в новых функциях 1С имеет индекс 1 (не 0, как в VBScript.RegExp)
ИскомыйТекст = РезультатПоиска.Группа(1);
Сообщить("Найденный текст (современные функции): " + ИскомыйТекст); // Должно вывести "Some text"
Иначе
Сообщить("Совпадение найдено, но групп захвата нет.");
КонецЕсли;
Иначе
Сообщить("Совпадение не найдено.");
КонецЕсли;
// Если нужно найти все совпадения, можно использовать функцию СтрНайтиВсеПоРегулярномуВыражению()
// или вызывать СтрНайтиПоРегулярномуВыражению() в цикле, передавая позицию начала поиска
В этом примере мы передаем исходную строку и регулярное выражение функции СтрНайтиПоРегулярномуВыражению(). Если совпадение найдено, функция возвращает объект РезультатПоиска. Важно обратить внимание, что в этом объекте группы захвата индексируются, начиная с 1 (Группа(1)), в отличие от VBScript.RegExp, где они начинались с 0.
Этот современный подход является предпочтительным при разработке на актуальных версиях платформы 1С, так как он обеспечивает лучшую производительность, совместимость и более удобный API для работы с регулярными выражениями.
Вне зависимости от выбранного вами подхода, будь то классический VBScript.RegExp, встроенные строковые функции или современные средства 1С 8.3.23+, мы рекомендуем использовать онлайн-тестеры регулярных выражений (например, regex101) или специализированные конструкторы (как DevTool1C) для проверки и отладки ваших шаблонов. Это значительно упрощает процесс разработки и помогает избежать ошибок.