При работе со строковыми данными в 1С часто возникает задача извлечь подстроку, которая может соответствовать нескольким шаблонам одновременно. В таких случаях критически важно определить приоритет выбора: если в строке присутствует расширенный набор данных, мы хотим получить его, а если только базовый — ограничиться им. В этой статье мы подробно разберем, как реализовать логику выбора по приоритету с использованием объекта VBScript.RegExp.
Прежде всего, нам необходимо понять, как движок регулярных выражений (NFA), используемый в Windows и 1С, обрабатывает оператор «или» (символ |). В отличие от некоторых других логических систем, в регулярных выражениях этот оператор является зависимым от порядка. Движок проверяет альтернативы слева направо и останавливается на первой, которая подошла под условия поиска.
Рассмотрим ситуацию, когда нам нужно выбрать между длинной строкой «ABCDE_1234>» и короткой «ABCDE_». Если мы напишем шаблон как (ABCDE_|ABCDE_1234>), движок найдет короткое совпадение и сразу же вернет результат, даже не пытаясь проверить вторую часть. Поэтому главное правило приоритета гласит: всегда ставьте самое специфичное (длинное) условие первым, а более общее (короткое) — последним.
Допустим, у нас есть входящая строка вида "ABCDE_1234>". Нам нужно извлечь данные по следующей логике:
>, мы забираем всю часть до него включительно.> нет, мы забираем только часть, заканчивающуюся на подчеркивание _.Проанализируем базовое решение, предложенное в обсуждении:
// Пример шаблона с жестко заданным текстом
Шаблон = "(ABCDE_1234>|ABCDE_)";
Этот вариант работает для статического текста, но в реальной разработке 1С содержимое строки (например, артикул или идентификатор) заранее неизвестно. Нам нужно составить универсальный паттерн.
Выясним причину, почему жесткое прописывание текста не подходит: данные могут меняться, и вместо «ABCDE» может прийти «1_Ф». Правильный подход заключается в использовании метасимволов и квантификаторов. Рассмотрим по шагам, как составить такой шаблон:
1. Сначала описываем приоритетный вариант (с символом >). Мы используем конструкцию, которая ищет любые символы до знака подчеркивания, затем цифры и завершающий символ.
2. Затем через | добавляем вариант поиска только до подчеркивания.
Пример реализации на встроенном языке 1С:
РегВыр = Новый COMОбъект("VBScript.RegExp");
РегВыр.IgnoreCase = Истина;
РегВыр.Global = Истина;
// Паттерн: (ЛюбыеСимволы_ЛюбыеСимволы>|ЛюбыеСимволы_)
// Используем [^>]* для предотвращения чрезмерного захвата
РегВыр.Pattern = "([^>]+_[^>]+>|[^>]+_)";
Текст = "ABCDE_1234>";
Результаты = РегВыр.Execute(Текст);
Если Результаты.Count > 0 Тогда
НайденноеЗначение = Результаты.Item(0).Value;
Сообщить("Результат: " + НайденноеЗначение);
КонецЕсли;
Посмотрим на ситуацию под другим углом. Вместо явного перечисления «или», мы можем использовать один паттерн с необязательной группой. Это более изящный способ решить задачу выбора по приоритету.
Разберем структуру паттерна ^.*?_([^>]*>)?:
^ — привязка к началу строки..*?_ — ленивый поиск любых символов до первого встреченного подчеркивания.([^>]*>)? — необязательная группа. Если в строке после подчеркивания есть последовательность, заканчивающаяся на >, она будет захвачена. Знак вопроса в конце делает всю группу необязательной.Важный момент: в VBScript.RegExp жадные квантификаторы (такие как *) пытаются захватить как можно больше текста. Если в вашей строке может быть несколько символов >, обязательно используйте исключающий набор [^>]* вместо точки .*, чтобы захват остановился на первом встреченном маркере.
Проанализируем, как правильно извлекать данные, если мы используем скобки для группировки. В 1С объект Match содержит коллекцию SubMatches, которая позволяет обращаться к частям строки, попавшим в скобки.
Текст = "1_Ф>";
РегВыр.Pattern = "(.*_)(.*>)?";
Matches = РегВыр.Execute(Текст);
Если Matches.Count > 0 Тогда
Match = Matches.Item(0);
// Если вторая группа (.*>) найдена, склеиваем результат
Если Match.SubMatches.Count > 1 И ЗначениеЗаполнено(Match.SubMatches.Item(1)) Тогда
ИтоговыйРезультат = Match.SubMatches.Item(0) + Match.SubMatches.Item(1);
Иначе
ИтоговыйРезультат = Match.SubMatches.Item(0);
КонецЕсли;
КонецЕсли;
Хотя VBScript.RegExp не поддерживает поиск назад (lookbehind), он отлично справляется с положительным просмотром вперед. Это позволяет проверить наличие символа впереди, не включая его в само совпадение, если это требуется по бизнес-логике.
Например, паттерн .*_(?=.*>) найдет часть строки до подчеркивания только в том случае, если где-то дальше в строке встречается символ >. Это мощный инструмент для ветвления логики внутри одного регулярного выражения.
Подводя итог, для реализации приоритетного выбора в 1С придерживайтесь следующих правил:
*? или +?, если вам нужно найти ближайший разделитель, а не последний в строке."ABC_DEF_123>"), чтобы убедиться, что логика захвата работает предсказуемо.Рассмотренные методы позволяют гибко управлять процессом парсинга строк, минимизируя количество условий Если...Иначе в программном коде 1С и перекладывая логику обработки на мощный механизм регулярных выражений.