В практике разработки на платформе 1С:Предприятие часто возникает задача динамической обработки текстов большой длины. Казалось бы, использование типа данных Строка с неограниченной длиной должно снимать все вопросы. Однако программисты нередко сталкиваются с ситуацией, когда при передаче данных между таблицами или загрузке из макетов текст внезапно обрезается (например, на 525-м символе или по длине самого первого загруженного значения). Рассмотрим подробнее, почему это происходит, проанализируем внутренние механизмы платформы и разберем способы решения этой проблемы.
Типичная ситуация: мы загружаем текст (например, пункты Устава или описание товаров) из макета в ТабличныйДокумент, затем с помощью ПостроителяОтчета преобразуем его в ТаблицуЗначений (ТЗ). На первый взгляд, всё работает корректно. Но как только мы пытаемся объединить несколько таких таблиц или скопировать данные в другую ТЗ, строки начинают обрезаться. Выясним причину этого поведения.
Проблема кроется в неявной типизации. Когда платформа создает колонки таблицы значений автоматически (например, при использовании ПостроительОтчета.Выгрузить() или ТЗ.Скопировать()), она анализирует исходные данные. Если в источнике колонка имела неявное ограничение длины, это ограничение «наследуется» новой таблицей. Даже если в метаданных целевого реквизита указано «неограниченная длина», данные могут быть испорчены еще на этапе подготовки в оперативной памяти.
Разберем ситуацию, когда ТабличныйДокумент преобразуется в ТЗ через ПостроительОтчета. Этот объект при выгрузке данных пытается самостоятельно определить типы колонок. Он сканирует содержимое ячеек, и если в первой строке или в определенной выборке максимальная длина строки составляет, к примеру, 525 символов, построитель может назначить колонке тип Строка с фиксированной длиной 525.
В результате, когда мы программно добавляем в эту же таблицу более длинный текст, платформа принудительно обрезает его в соответствии с описанием типов, которое было сформировано «по шаблону» первых строк. Это происходит незаметно для разработчика, так как ошибка не генерируется — данные просто усекаются.
Многие разработчики используют метод ТЗ.Скопировать() для создания новой таблицы на основе существующей. Посмотрим на пример:
// Предположим, ТЗ_Источник получена из Построителя отчета
ТЗ_Итоговая = ТЗ_Источник.Скопировать();
Здесь кроется главная ловушка. Метод Скопировать() копирует не только данные, но и полное описание типов колонок со всеми квалификаторами. Если ТЗ_Источник имела колонку «Содержание» с ограничением в 500 символов, то и ТЗ_Итоговая будет иметь точно такое же ограничение. При попытке дописать в ТЗ_Итоговая строки длиной в 2000 символов через метод ЗаполнитьЗначенияСвойств() или прямое присваивание, текст будет обрезан.
Для того чтобы гарантировать отсутствие лимитов, мы должны принудительно инициализировать структуру таблицы значений. Вместо использования автоматического создания колонок, применим объект ОписаниеТипов. Проанализируем правильный подход к созданию колонки с неограниченной строкой:
// Правильное создание описания типа для неограниченной строки
КвалификаторыС = Новый КвалификаторыСтроки(0); // 0 означает неограниченную длину
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Строка"));
ОписаниеТипаСтрока = Новый ОписаниеТипов(МассивТипов, , КвалификаторыС);
// Создаем таблицу и добавляем колонку явно
ИтоговаяТаблица = Новый ТаблицаЗначений;
ИтоговаяТаблица.Колонки.Добавить("СодержаниеПункта", ОписаниеТипаСтрока);
Использование КвалификаторыСтроки(0) — это самый надежный способ сообщить платформе, что данные в этой колонке не должны ограничиваться по длине. Если просто написать Новый ОписаниеТипов("Строка"), это тоже сработает в большинстве случаев, но явное указание квалификатора считается более хорошим тоном в сложных алгоритмах.
Как показал опыт практикующих программистов, иногда даже при правильном описании типов метод Загрузить() может вести себя непредсказуемо, если структуры источника и приемника не идентичны. Рассмотрим ситуацию, когда помогло использование цикла:
// Вместо Объект.СодержаниеРазделов.Загрузить(ИтоговаяТаблица);
// Используем перебор строк
Для Каждого СтрокаИст Из ИтоговаяТаблица Цикл
НоваяСтрока = Объект.СодержаниеРазделов.Добавить();
// При прямом присваивании происходит приведение типов к типам приемника
НоваяСтрока.СодержаниеПункта = СтрокаИст.СодержаниеПункта;
НоваяСтрока.НаименованиеРаздела = СтрокаИст.НаименованиеРаздела;
КонецЦикла;
Почему это работает? При выполнении метода Загрузить() платформа выполняет высокоуровневое копирование областей памяти. Если в источнике тип был «Строка 525», а в приемнике «Строка неограниченно», в некоторых версиях платформы может произойти копирование именно ограниченного значения. В цикле же, при выполнении оператора =, происходит явное присваивание значения переменной. Если реквизит табличной части СодержаниеПункта в метаданных определен как неограниченная строка, платформа примет данные любой длины без обрезания.
Проанализируем основные правила, которые помогут избежать потери данных:
ОписаниеТипов для строк всегда учитывайте, что пустой конструктор может накладывать системные ограничения. Используйте Новый КвалификаторыСтроки(0).Рассмотрим еще один важный технический нюанс. При отладке может показаться, что тип значения в колонке — Строка, и ТипЗнч() это подтверждает. Однако ТипЗнч() не показывает квалификаторы (длину). Чтобы увидеть реальное ограничение, нужно анализировать объект КолонкаТаблицыЗначений.ТипЗначения, где в массиве КвалификаторыСтроки будет указана истинная допустимая длина.
Соблюдение этих правил и понимание механизмов наследования типов при копировании структур позволит вам корректно обрабатывать тексты любого объема, сохраняя целостность данных вашего приложения.