При разработке отчетов в Системе компоновки данных (СКД) часто возникает необходимость разобрать текстовую строку и извлечь из нее определенную информацию. Например, из строки вида "Поездка в Москву 25/01/2017-28/01/2017 5 д." требуется получить количество дней (число 5) для дальнейшего использования в математических расчетах и ресурсах отчета.
Рассмотрим, с какими ограничениями платформы можно столкнуться при решении этой задачи и какими способами можно правильно выделить нужный фрагмент текста.
Попытки выполнить сложный парсинг текста непосредственно в языке запросов 1С сталкиваются со следующими техническими ограничениями:
ВЫРАЗИТЬ(... КАК ЧИСЛО) в языке запросов применяется исключительно для ограничения длины или приведения составных типов данных в колонках базы данных. Она не предназначена для преобразования строки вида "5" в числовое значение 5.СтрНайти, СтрЗаменить или возможность использования регулярных выражений.Число() напрямую в выражении.Разберем подробнее варианты решения этой проблемы.
Наиболее удобный способ решения задачи внутри СКД — вынести логику обработки строки в экспортную функцию общего модуля и вызвать ее в вычисляемом поле отчета.
Создадим в общем модуле (с включенными свойствами «Клиент» и «Сервер» или «Сервер») экспортную функцию. Функция разбивает строку на массив слов по разделителю-пробелу и возвращает необходимое значение:
Функция ИзвлечьКоличествоДней(ТекстНаименования) Экспорт
Если Не ЗначениеЗаполнено(ТекстНаименования) Тогда
Возврат 0;
КонецЕсли;
// Разбиваем строку в массив подстрок по пробелу
МассивПодстрок = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(СокрЛП(ТекстНаименования), " ");
КоличествоЭлементов = МассивПодстрок.Количество();
Если КоличествоЭлементов < 2 Тогда
Возврат 0;
КонецЕсли;
// Предпоследнее слово (индексация массива с 0): КоличествоЭлементов - 2
ЗначениеТекст = МассивПодстрок[КоличествоЭлементов - 2];
// Преобразуем полученный фрагмент в число
Попытка
Возврат Число(ЗначениеТекст);
Исключение
Возврат 0;
КонецПопытки;
КонецФункции
Шаги по настройке отчета в СКД:
КоличествоДней.ИмяОбщегоМодуля.ИзвлечьКоличествоДней(Наименование).КоличествоДней выражение рассчитываемых итогов: Сумма(КоличествоДней).Если в конфигурации используется Библиотека Стандартных Подсистем (БСП), можно применить готовые функции из модуля СтроковыеФункцииКлиентСервер.
Разберем пример извлечения чисел из текста без явного разделения строки по пробелам:
Функция ПолучитьЧислоИзСтроки(ТекстСтроки) Экспорт
// Получаем массив всех числовых фрагментов из строки
МассивЧисел = СтроковыеФункцииКлиентСервер.ВыделитьЧислаИзСтроки(ТекстСтроки);
Если МассивЧисел.Количество() > 0 Тогда
// Возвращаем последнее число из найденных в строке
Возврат МассивЧисел[МассивЧисел.Количество() - 1];
КонецЕсли;
Возврат 0;
КонецФункции
Функция СтроковыеФункцииКлиентСервер.СтрокаВЧисло() также помогает безопасно сконвертировать строку в числовой тип с автоматическим удалением непечатаемых символов и пробелов.
Если формат исходных строк нестандартен или логика работы с вычисляемыми полями перегружает компоновку данных, оптимальным архитектурным решением будет предварительная обработка данных в коде 1С перед вызовом СКД.
Схема реализации выглядит следующим образом:
КоличествоДней (тип Число).Таблицу Значений.
ТаблицаДанных = Запрос.Выполнить().Выгрузить();
ТаблицаДанных.Колонки.Добавить("КоличествоДней", Новый ОписаниеТипов("Число"));
Для Каждого Стр Из ТаблицаДанных Цикл
// Обработка строки средствами 1С
МассивСлов = СтрРазделить(Стр.Наименование, " ", Ложь);
Если МассивСлов.Количество() >= 2 Тогда
Стр.КоличествоДней = Число(МассивСлов[МассивСлов.Количество() - 2]);
КонецЕсли;
КонецЦикла;
ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("ТаблицаДанных", ТаблицаДанных);
// Дальнейшая выгрузка в СКД через КомпоновщикМакета
Данный подход позволяет полностью обойти ограничения языка запросов 1С и выполнять любые сложные манипуляции со строками на этапе подготовки данных.