При работе со справочниками номенклатуры, особенно в фармацевтической или технической отраслях, часто возникает задача очистки наименования от дополнительной информации, указанной в конце строки в скобках. Например, когда нужно превратить «Авелокс (Bayer AG (рецепт.))» в просто «Авелокс». Сложность заключается в том, что внутри основных скобок могут находиться вложенные, а сама платформа 1С 8.2 имеет ограниченный набор строковых функций по сравнению с современными версиями 8.3.
В этой статье мы подробно разберем, как реализовать надежный алгоритм обрезки строки, который корректно обрабатывает вложенные структуры и учитывает специфику платформы 8.2.
Прежде чем переходить к коду, проанализируем инструменты, которые нам доступны. В старых версиях платформы (8.2 и ранние релизы 8.3):
СтрНайти, позволяющая искать символы с конца строки.СтрРазделить, которая могла бы разбить строку в массив по разделителю.СтрЗаменить не позволяет ограничивать количество замен.Поэтому стандартный подход через поиск позиции символа ( слева направо не сработает для строк вида «Амлотоп №30 (амлодипин) (Нижфарм ОАО)», так как нам нужно сохранить информацию о веществе, но удалить производителя.
Рассмотрим наиболее производительный и логически верный метод — обход строки справа налево с использованием счетчика «баланса». Суть метода заключается в том, что мы идем от последнего символа к первому и ищем ту открывающую скобку, которая закрывает всю конечную конструкцию.
Разберем алгоритм по шагам:
СокрП.) и уменьшаться при встрече открывающей (.Посмотрим на программную реализацию этого метода:
Функция ОбрезатьСкобкиСправа(Знач ИсходнаяСтрока) Экспорт
ИсходнаяСтрока = СокрП(ИсходнаяСтрока);
ДлинаСтроки = СтрДлина(ИсходнаяСтрока);
// Проверим, заканчивается ли вообще строка на скобку
Если Прав(ИсходнаяСтрока, 1) <> ")" Тогда
Возврат ИсходнаяСтрока;
КонецЕсли;
СчетчикБаланса = 0;
ПозицияОбрезания = 0;
// Цикл с конца строки к началу
Для НомерСимвола = -ДлинаСтроки По -1 Цикл
Индекс = -НомерСимвола; // Получаем текущий индекс символа
Символ = Сред(ИсходнаяСтрока, Индекс, 1);
Если Символ = ")" Тогда
СчетчикБаланса = СчетчикБаланса + 1;
ИначеЕсли Символ = "(" Тогда
СчетчикБаланса = СчетчикБаланса - 1;
КонецЕсли;
// Если баланс сошелся в ноль, значит нашли начало закрывающей группы
Если СчетчикБаланса = 0 Тогда
ПозицияОбрезания = Индекс;
Прервать;
КонецЕсли;
КонецЦикла;
Если ПозицияОбрезания > 0 Тогда
// Обрезаем строку и удаляем возможный пробел перед скобкой
Возврат СокрП(Лев(ИсходнаяСтрока, ПозицияОбрезания - 1));
Иначе
Возврат ИсходнаяСтрока;
КонецЕсли;
КонецФункции
Проанализируем ситуацию: в примере «Авелокс (Bayer AG (рецепт.))» цикл встретит первую закрывающую скобку (баланс = 1), затем вторую (баланс = 2), затем встретит открывающую у слова «рецепт.» (баланс = 1) и, наконец, встретит открывающую перед «Bayer» (баланс = 0). В этот момент переменная ПозицияОбрезания зафиксирует начало всей конструкции, и мы корректно получим результат.
Если нам нужно более гибкое решение (например, удалять только если в скобках есть определенные слова), мы можем воспользоваться объектом VBScript.RegExp. Несмотря на то, что это внешнее обращение к Windows API, оно работает крайне быстро и эффективно в 1С 8.2.
Рассмотрим пример кода:
Функция УдалитьПоследниеСкобкиРегуляркой(ИсходнаяСтрока)
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.IgnoreCase = Истина;
RegExp.Global = Ложь; // Нам нужно только последнее вхождение
// Паттерн ищет пробел, затем открывающую скобку,
// любые символы внутри (включая вложенные скобки) и закрывающую скобку в конце строки
RegExp.Pattern = "\s*\([^)]*(\([^)]*\)[^)]*)*\)$";
Результат = RegExp.Replace(СокрП(ИсходнаяСтрока), "");
Возврат Результат;
КонецФункции
Этот метод хорош тем, что он одной строкой кода решает проблему пробелов перед скобками и корректно определяет границы вложенности без написания сложных циклов.
В некоторых случаях, когда требуется обработать сразу тысячи строк (например, при загрузке прайс-листа), может быть полезен метод обработки через временные таблицы в запросе. Выясним причину, почему это иногда предпочтительнее: SQL сервер обрабатывает строковые операции на своей стороне быстрее, чем интерпретатор 1С в цикле перебора объектов.
Как мы видели в обсуждении, можно построить запрос, который через ПОДСТРОКА анализирует символы. Однако стоит признать, что для 1С 8.2 такой запрос получается крайне громоздким (как в сообщении 16) из-за необходимости эмулировать индексы символов через соединение с таблицей чисел. Мы рекомендуем использовать этот метод только в исключительных случаях, когда работа со строками в коде становится «узким местом» производительности.
Проанализируем дополнительные нюансы, которые могут возникнуть при использовании данных алгоритмов:
(таб.) или (амп.). Если ваша задача — удалить производителя, но оставить форму выпуска, вам придется добавить в цикл проверку содержимого найденной группы скобок.СокрЛП или СокрП после обрезки, так как между основным названием и скобкой почти всегда стоит пробел, который после удаления скобок станет «хвостом» строки.Подводя итог, можно сказать, что для платформы 1С 8.2 оптимальным является Решение 1. Оно не требует внешних компонентов, стабильно работает в тонком и толстом клиенте и на 100% предсказуемо при наличии вложенных скобок.