При выгрузке данных, обмене с внешними системами или формировании печатных форм разработчики часто сталкиваются с необходимостью обработать строковые данные. Типичный пример — когда в реквизите или представлении хранится объединенная информация (например, серия номенклатуры и срок годности через запятую: "010818, 31.08.2021"), а для выгрузки в XML требуется получить только первую часть строки до запятой.
В этой статье мы подробно разберем несколько способов решения этой задачи на платформе «1С:Предприятие 8», проанализируем их плюсы и минусы, оценим производительность и рассмотрим архитектурно правильный подход к получению таких данных.
Этот способ основан на базовых функциях работы со строками. Мы находим позицию символа-разделителя в строке, а затем берем левую часть строки до этой позиции. Рассмотрим базовую реализацию:
ИсходнаяСтрока = Строка(Выборка.Серия);
ПозицияЗапятой = СтрНайти(ИсходнаяСтрока, ",");
Если ПозицияЗапятой > 0 Тогда
Результат = Лев(ИсходнаяСтрока, ПозицияЗапятой - 1);
Иначе
Результат = ИсходнаяСтрока;
КонецЕсли;
Важные нюансы реализации:
Если ПозицияЗапятой > 0. Если в строке не окажется запятой, функция СтрНайти() вернет значение 0. Если передать его напрямую в функцию обрезки строк (как в выражении ПозицияЗапятой - 1), мы получим отрицательный индекс -1, что вызовет ошибку выполнения в старых версиях платформы или вернет пустую строку.СокрЛП() (сокращение незначащих левых и правых пробелов).Для компактности этот алгоритм можно записать с использованием тернарного оператора:
СтрокаСерии = Строка(Выборка.Серия);
Позиция = СтрНайти(СтрокаСерии, ",");
СерияДляВыгрузки = СокрЛП(?(Позиция = 0, СтрокаСерии, Лев(СтрокаСерии, Позиция - 1)));
Начиная с версии платформы 8.3.6, в 1С доступна удобная встроенная функция СтрРазделить(), которая разбивает исходную строку на массив подстрок по указанному разделителю.
Рассмотрим, как применить ее для нашей задачи:
СтрокаСерии = Строка(Выборка.Серия);
ЧастиСтроки = СтрРазделить(СтрокаСерии, ",");
Если ЧастиСтроки.Количество() > 0 Тогда
СерияДляВыгрузки = СокрЛП(ЧастиСтроки[0]);
Иначе
СерияДляВыгрузки = "";
КонецЕсли;
Подводные камни функции СтрРазделить():
СтрРазделить(СтрокаСерии, ",")[0]. Однако, если ваша конфигурация работает на более старой версии платформы или использует режим совместимости ниже 8.3.15, такой код вызовет синтаксическую ошибку при компиляции.СтрРазделить() воспринимается платформой как отдельный разделитель. Если вы укажете в качестве разделителя комбинацию запятой и пробела ", ", строка будет разбита и по запятой, и по пробелу отдельно. Строка "010818, 31.08.2021" в этом случае разобьется не на две части, а на три ("010818", пустая строка/пробел, "31.08.2021"). Для одиночного символа (например, только запятой) этот метод работает отлично.Если вам требуется корректно разделить строку по многосимвольному разделителю (например, строго по комбинации «запятая + пробел» ", "), стандартная функция СтрРазделить() не подойдет. В этом случае на помощь приходит Библиотека Стандартных Подсистем (БСП), которая интегрирована практически во все типовые конфигурации 1С.
Мы можем использовать функцию из общего модуля СтроковыеФункцииКлиентСервер:
СтрокаСерии = Строка(Выборка.Серия);
МассивЧастей = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(СтрокаСерии, ", ");
Если МассивЧастей.Количество() > 0 Тогда
СерияДляВыгрузки = МассивЧастей[0];
Иначе
СерияДляВыгрузки = "";
КонецЕсли;
Этот метод полностью безопасен, корректно обрабатывает многосимвольные разделители целиком и работает на любых версиях платформы 1С.
Еще один интересный и лаконичный способ — временное превращение обычной строки в многострочную путем замены разделителя на символ перевода строки (Символы.ПС). После этого мы можем легко прочитать первую строку.
СтрокаСерии = Строка(Выборка.Серия);
МногострочныйТекст = СтрЗаменить(СтрокаСерии, ",", Символы.ПС);
СерияДляВыгрузки = СокрЛП(СтрПолучитьСтроку(МногострочныйТекст, 1));
Этот код привлекателен тем, что не требует написания условий Если ... КонецЕсли и работает корректно даже в том случае, если запятой в исходной строке не было (тогда в многострочном тексте будет всего одна строка, которая и вернется).
Если вы обрабатываете небольшой объем данных (например, при сохранении одной формы), разница в скорости работы алгоритмов будет незаметна. Однако, если выполняется выгрузка сотен тысяч позиций номенклатуры в XML-файл, производительность строковых функций становится критически важной.
Проанализируем работу механизмов платформы при больших нагрузках:
Массив, заполнить его строковыми элементами, а затем уничтожить этот массив при выходе переменной из области видимости. При итерации по большому циклу это создает существенную нагрузку на сборщик мусора.Вывод: Для оптимизации производительности в высоконагруженных циклах обмена данными всегда выбирайте связку Лев() и СтрНайти() с проверкой на вхождение символа.
Программное разделение строк в коде — это всегда потенциальная точка отказа. Если завтра изменятся правила ведения учета или формат ввода данных в базу, логика работы строковых функций может нарушиться.
Поскольку данные выбираются из запроса (Выборка.Серия), появление запятой и срока годности в строке означает, что в запросе, скорее всего, извлекается текстовое представление ссылки на элемент справочника (например, справочник «Серии номенклатуры»), наименование которого формируется динамически.
Вместо того чтобы парсить готовую строку в коде выгрузки, правильнее изменить сам запрос и получить чистое значение номера серии напрямую из реквизита объекта:
ВЫБРАТЬ
ТаблицаТовары.Номенклатура КАК Номенклатура,
ТаблицаТовары.Серия.Номер КАК НомерСерии, // Получаем чистый номер серии
ТаблицаТовары.Серия.СрокГодности КАК СрокГодности
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ТаблицаТовары
ГДЕ
Ссылка = &Ссылка
При таком подходе в выборку запроса попадет чистое значение серии. В коде выгрузки XML вам больше не понадобятся функции обработки строк, а код станет максимально простым, быстрым и устойчивым к изменениям:
ЗаписьXML.ЗаписатьНачалоЭлемента("SERIES");
ЗаписьXML.ЗаписатьТекст(СокрЛП(Выборка.НомерСерии));
ЗаписьXML.ЗаписатьКонецЭлемента();